Бегущая строка (табло) на JavaScript

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

Как-то раз, сидя в вагоне «Русича» и глядя на табло, я подумал, а как интересно кодят его сообщения и как вообще оно работает в плане отображения текста в виде точек и, самое главное, его анимации.

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

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

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

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

Доведя работу до точки, а случилось это только днём 31 декабря, я отправился на празднование Нового Года к другу. Новый Год мы встретили чудесно в замечательной компании, а на следующий день я отправил ссылку на страничку близким друзьям.

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

Страничка, zip, GitHub.

About table with `display: block` and `overflow-y: auto`

Few days ago my college and I had a page with an extra wide table on it which breaked the page body that is the horizontal scroll appeared on the whole page. We solved the problem with adding the following style on the table: { display: block; overflow-y: auto; max-width: 10px /* any low value here */ }.

But as you may know the tables are not supposed to wear display: block CSS declaration and that was why we failed trying to create something universal for all the tables.

We decided we have to check if the table is extra wide or not and found a quite simple solution, this small: jquery plugin from the answer:

$.fn.hasOverflow = function() { 
    var $this = $(this); 
    var $children = $this.find('*'); 
    var len = $children.length; 
 
    if (len) { 
        var maxWidth = 0; 
        var maxHeight = 0 
        $children.map(function() { 
            maxWidth = Math.max(maxWidth, $(this).outerWidth(true)); 
            maxHeight = Math.max(maxHeight, $(this).outerHeight(true)); 
        }); 
 
        return maxWidth > $this.width() || maxHeight > $this.height(); 
    } 
 
    return false; 
};

We edited it first to make it taking only width in account and then used it in the following way: $('body').hasOverflow().

But it did not work somethimes (probably something more has been changed on the page) and what is also bad thing is that it selects so huge amount of elements on a big pages that it really impacts the performance. But the next day we started to solve it again and in a few minutes we came with a very simple solution:

$(window).width() < $('body')[0].scrollWidth

It works fine by now! We needed just to sleep on it

Про table с `display: block` и `overflow-y: auto`

Недавно была у нас страничка, там появлалась очень широкая таблица, которая разрывала страницу, т.е. заставляла появиться горизонтальный скролл на body. Мы решили эту проблему добавлением стиля на таблицу, а именно { display: block; overflow-y: auto; max-width: 10px /* любая маленькая величина */ }.

Но как известно, таблицы не предназначены для того, чтобы на них вешался display: block, поэтому несмотря на многочисленные попытки сделать что-то универсальное, у других, более узких таблиц, возникли проблемы.

Мы решили, что надо определять, рвёт ли таблица страницу, или не рвёт.

Тут же нашли решение – вот такой компактный jquery-плагин здесь:

$.fn.hasOverflow = function() { 
    var $this = $(this); 
    var $children = $this.find('*'); 
    var len = $children.length; 
 
    if (len) { 
        var maxWidth = 0; 
        var maxHeight = 0 
        $children.map(function() { 
            maxWidth = Math.max(maxWidth, $(this).outerWidth(true)); 
            maxHeight = Math.max(maxHeight, $(this).outerHeight(true)); 
        }); 
 
        return maxWidth > $this.width() || maxHeight > $this.height(); 
    } 
 
    return false; 
};

Мы подправили его так, чтобы рассматривалась только ширина и использовали его следующим образом: $('body').hasOverflow().

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

$(window).width() < $('body')[0].scrollWidth

Пока исправно работает! Утро вечера мудренее

Поведение `for (var i in arr)` в IE8 после расширения Array.prototype

Недавно решил посмотреть, как дела у моего jQuery плагина в IE8. Я думаю, предсказать что было дальше не сложнее, чем предугадать, чем закончится классическая голливудская мелодрама – в скрипте обнаружились ошибки.

Из-за убогости консоли я долго думал в чём дело и столкнулся с необычным явлением. Как известно, IE8, как и все другие десктопные браузеры от Microsoft с момента своего появления является сильно устаревшим браузером. В частности, в нём нет ряда методов из Array.prototype, таких как весьма удобный и относительно часто используемый indexOf(obj). Естественно я знал это и позаботился, добавив к проекту небольшой скрипт missed.js, который кочует у меня из проекта в проект, разрешая проблему недостающего функционала в IE8. Этот скрипт – классическое решение, он содержит ряд объявлений вида:

if (!Array.prototype.indexOf) { 
    Array.prototype.indexOf = function(...) {...} 
}

Не знаю, почему я впервые наткнулся на ошибку только недавно, но в общем это был первый раз, когда я увидел, как после этого будет использоваться код вида for (var i in arr), в случае, когда arr – массив.

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

Обсудив это с коллегой я пришёл к выводу, что лучше для массивов всегда использовать код вида for (var i = 0; i < arr.length; i++).

`for (var i in arr)` behavior in IE8 after extending Array.prototype

Few days ago I decided to see how is my jQuery plugin doing in IE8. I think to predict what happened next is not much more difficult than to guess how a classical Hollywood melodrama ends. There were errors in the script.

Due to the IE console is incredibly poor I spent a lot of time considering what was going and faced an interesting phenomenon. As you know, IE8 like all other desktop browsers from Microsoft corporation is heavily outdated since the time it was released. For instance, there is luck of some methods from Array.prototype, such as very handy and quite popular indexOf(obj). I knew about that of course and that is why I added my missed.js script I use in many projects for a long time, which is a collection of declarations like:

if (!Array.prototype.indexOf) { 
    Array.prototype.indexOf = function(...) {...} 
}

This is strange it was the first time I faced the error only now, but anyhow it was the first time I saw how for (var i in arr) construction will work after extending Array.prototype if
arr is an array.

It turned out (what is clear on the one hand, but is not so predictable on the other) that in this case, i takes not only indices valus, but also not native methods from its (Array) prototype. That is why using such construction is not impossible without additional editings like doint continue if i is not a mumber.

After talking to my college about that I made a conclusion that if you dealing with arrays it is always better to use for (var i = 0; i < arr.length; i++) code.

MySQL and www directory backup with Task Scheduler and a BATCH script

The other day I needed to set daily files and databases backup on the web server. It was Windows Server 2008 R2 so the main tools I used were bat-scripts and Task Scheduler.

Maybe BATCH code and exported tasks for Windows Task Scheduler I’ve created would help someone with managing jobs like this.

Of couse it would be great to add an ftp-upload feature for storing composed files somewhere else. I hope I’ll add it sometime.

All stuff in the archive.

This BATCH script uses 7z archiver, so be sure you have it or just comment the zipping lines and add something of your own.

Creating a bunch of directories for file storage using BATCH script

The other day I needed to create lots of directories for file storage with the following structure:

Создание множества папок для файлового хранилища, BATCH

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

Автоматический бэкап базы и директории с сайтом с помощью BATCH и планировщика заданий

Недавно была необходимость настроить ежедневный бэкап определённых файлов и баз данных на сервере с запущенным сайтом. Поскольку речь шла о Windows Server 2008 R2, то основными инструментами должны были стать bat-скрипты и планировщик заданий.

Может кому-нибудь пригодятся коды этих скриптов и заодно экспортированные xml-ники задачи.

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

Вся информация в архиве.

Этот скрипт использует 7z, так что убедитесь, что он есть на сервере, либо закомментируйте его вызов и добавьте туда ваши команды.

Создание превьюшек в PHP

Создать экскиз-превью по изображению в PHP – задача тривиальная, необходимо только воспользоваться встроенными функциями imagecopyresampled() и imagejpeg().

Однако есть две вещи, которые...

Однако есть две вещи, которые хочется улучшить в этом процессе:

  1. Сделать изображение квадратным, обрезав лишнее слева и справа или сверху и снизу в равных количествах в случае, если изображение вытянуто в ширину, либо в высоту, соответственно.
  2. Зашарпить изображение.

Очень просто всё сделать становится с помощью этого класса:

/**
 * Пример использования:
 * <code>
 * $cover = new Cover("/images/cover.jpg");
 * $img = $cover->getPreparedImage();
 * imagejpeg($img, "/images/cover_preview.jpg", 96);
 * </code>
 * Либо сразу:
 * <code>
 * $cover = new Cover("/images/cover.jpg");
 * $cover->prepareAndWrite("/images/cover_preview.jpg");
 * </code>
 * 
 * http://kremer.pro/
 */
class Cover {

    /**
     * Сторона конечного изображения в пикселях по умолчанию.
     */
    const PREVIEW_SIZE = 128;

    /**
     * Максимальный размер исходного изображения в пикселях
     * (Ограничение на работу с изображением).
     */
    const MAX_ORIG_SIZE = 2000;

    const DEFAULT_JPEG_QUALITY = 92;

    private $size;

    /**
     * @param string $path путь к изображению
     * @param string $previewSize размер конечного изображения
     */
    public function __construct($path, $previewSize = self::PREVIEW_SIZE) {
        $this->img = imagecreatefromjpeg($path);
        if (!$this->img)
            throw new Exception("Unable to create from jpeg");
        $this->size = $previewSize;
    }

    public function __destruct() {
        imagedestroy($this->img);
    }

    /**
     * Шарпит изображение.
     */
    private function sharpen() {
        $filter = array ( // стандартный вид шарп-матрицы
            array( 0,  -1,   0),

            array(-1,   9,  -1),

            array( 0,  -1,   0),
        );
        $divisor = array_sum(array_map('array_sum', $filter));

        if (!imageconvolution($this->img, $filter, $divisor, 0))
            throw new Exception("Unable to sharpen");
    }

    /**
     * Создаёт незашарпленное квадратное превью.
     */
    private function cropAndResize() {
        $img_preview = imagecreatetruecolor($this->size, $this->size);

        $width_orig = imagesx($this->img);
        $height_orig = imagesy($this->img);

        if ($width_orig <= self::PREVIEW_SIZE && $height_orig <= self::PREVIEW_SIZE) {
            // нет смысла растягивать, лучше сразу зашарпить маленькое
            return;
        }
        if ($width_orig > self::MAX_ORIG_SIZE || $height_orig > self::MAX_ORIG_SIZE)
            throw new Exception("Image too lagre to process it");

        // генерируем квадратное изображение, обрезая либо полосы слева и справа,
        // либо серху и снизу:
        if ($width_orig > $height_orig) {
            $a = round(($width_orig - $height_orig)/2);//x координата, откуда режем
            if (!imagecopyresampled($img_preview, $this->img,
                0,              0,              $a,             0,
                $this->size,    $this->size,    $height_orig,    $height_orig)) {
                throw new Exception("Unable to resample");
            }
        } else {
            $a = round(($height_orig - $width_orig)/2);//y координата, откуда режем
            if (!imagecopyresampled ($img_preview, $this->img,
                0,              0,              0,              $a,
                $this->size,    $this->size,    $width_orig,    $width_orig)) {
                throw new Exception("Unable to resample");
            }
        }

        $this->img = $img_preview; // теперь в объекте уже превьюшка
    }

    /**
     * @return resource $img - сжатое и зашарпленное изображение.
     * Выбрасывает исключения в случаях, если обработать изображение не удалось.
     */
    public function getPreparedImage() {
        $this->cropAndResize();
        $this->sharpen();
        return $this->img;
    }

    /**
     * Сразу делает превью и записывает его в файл.
     * Выбрасывает исключения в случаях, если обработать изображение не удалось.
     * После записи освобождает resource с изображением.
     */
    public function prepareAndWrite($path, $q = self::DEFAULT_JPEG_QUALITY) {
        imagejpeg($this->getPreparedImage(), $path, $q);
        imagedestroy($this->img);
    }

    public function clear() {
        imagedestroy($this->img);
    }

    private $img; // resource изображение, с которым работаем
}