вторник, 27 декабря 2011 г.

По-моему, отличный мотиватор!

среда, 14 декабря 2011 г.

Лидеров не обязательно искать на площади или в твиттере

Лидеров не обязательно искать на площади или в твиттере:

В подъезде нашего дома жила бабушка. Бабушка Люба. Ей было 97 лет. Милая, приятная старушка, всегда в хорошем настроении, улыбчивая и приветливая. Для меня она – Просветленный Лидер. Спокойно! Я в с своём уме, и не падала ниц, когда видела её, сидящей на скамеечке около подъезда. Объясню почему я так думаю.


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


В подъезд стали проникать шумные подростки. Бабушка Люба вышла и … предложила им воды или чаю. Они долго смеялись. Пообрывали цветы, и перевернули рамки.


На следующий день, она снова поставила цветы, вернула рамкам прежний вид и положила на подоконник книги. Классику. Пришли подростки. Галдели, шумели. Она вышла и … предложила им чаю со своими плюшками, аппетитными и вкусно пахнущими. Ребята не смогли отказаться. И даже уволокли с собой книги с обещанием прочитать. Цветы они не тронули, рамки тоже.


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


Ребята стали приходить в подъезд каждый день, соседи возмущались, даже как-то вызвали милицию, но бабушка сказала, что это мол к ней, её ученики пришли за книжками, раздала при милиционерах книги растерянным подросткам и проводила милицию «С богом!» .


В подъезде появился шкаф с книгами. И рядом объявление: » Просьба! Если есть у вас дома интересные и важные книги, уже прочитанные вами, поделитесь! Будьте добры! А те, кто взял почитать, пожалуйста, верните для тех, кому так же это может быть нужно и важно!« Шкаф заполнился книгами. Цветы появились на подоконниках на всех этажах. Красивые рамки с цитатами тоже. Каждый вечер входную дверь в подъезд стали оставлять открытой. Вечером можно было увидеть на лестницах подростков, читающих книги. Бабушка положила на подоконник несколько фонариков, чтобы им было удобнее читать. Дети сидели в подъезде с включенными фонариками, и в нем было светлее, чем обычно.


Бабушка умерла. На первом этаже нашего дома открыли Клуб для детей и подростков. С библиотекой и цветами на подоконниках. Символом Клуба стал фонарик…


источник

пятница, 25 ноября 2011 г.

Расстановка точек над onmousewheel и немного о луковом супе

Хабра. Автор hautamaki предлагает ознакомиться с его способом обрабатывать скролы на элементах без скроллирования окна. А в дополнение предлагает интересный рецепт лукового супа! ;) Даже, если вам не интересен javascript, то ради рецепта л.с. статью можно и почитать. :)

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

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

Забегая вперёд, скажу, что этот экперимент удался вполне, а итоговый результат работает во всех десктопных браузерах, начиная с IE7 (по идее, должно работать и в шестом, но сейчас нет возможности это проверить). Также, хочу выразить благодарность поисковой системе Гугл. Без неё жизнь была бы соткана из уныния и отчаяния.

Сначала я решил задачу ловли onmousewheel для всего окна (объекта window). Решилось так:
window.onload = function() {
    if (window.addEventListener) window.addEventListener("DOMMouseScroll", mouse_wheel, false);
    window.onmousewheel = document.onmousewheel = mouse_wheel;
}

Первая строчка поймает событие в Firefox и веб-китовских браузерах (Chrome, Safari), вторая нужна для ловли в Опере и IE. Реакция на событие — функция mouse_wheel(), которая выглядит пока что следующим образом:
var mouse_wheel = function(event) {
    if (false == !!event) event = window.event;
    var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
}

С первой строкой всё ясно — это костылёк для IE. О второй строке расскажу подробнее. В ней я определяю, в каком направлении вращается колесо. Все браузеры кроме Firefox передают направление в свойстве wheelDelta как равное 120 или -120 в зависимости от того, куда крутится колесо. Мозилла же оперирует свойством detail, присваивая ему значения -3 или 3, соответственно. Да, именно так, знак не совпадает. Поэтому второй строчкой я привожу данные разных браузеров к +1, если колесо крутится вверх (от себя) и -1, если колесо крутится вниз (на себя). Ну и надеваю презерватив в виде «|| false» на случай ядерной войны или ещё какой-нибудь экзотики.

Программа минимум выполнена. Я ловлю событие прокрутки колеса мыши во всех браузерах и даже знаю в каком направлении крутится колесо. Едем дальше.

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

image

Действовать я решил самым простым и может даже слегка кондовым способом: завёл глобальную переменную wheel_handle (null или другое false-like значение по умолчанию), которой при событии onmouseover на определённом блоке будет присваиваться некая функция, определённая заранее. Выполняться функция будет при срабатывании onmousewheel и в качестве параметра в неё будет передаваться направление прокрутки. Код стал выглядеть так:
var wheel_handle = null;

var mouse_wheel = function(event) {
    if (false == !!event) event = window.event;
    var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
    if (direction && !!wheel_handle && typeof wheel_handle == "function") {
        wheel_handle(direction);
    }
}

var set_handle = function(id, func) {
    document.getElementById(id).onmouseover = function() {
        wheel_handle = func;
    }
    document.getElementById(id).onmouseout = function() {
        wheel_handle = null;
    }
}

window.onload = function() {
    if (window.addEventListener) window.addEventListener("DOMMouseScroll", mouse_wheel, false);
    window.onmousewheel = document.onmousewheel = mouse_wheel;
} 

В принципе, всё должно быть понятно, но на всякий случай. Функция mouse_wheel при наличие direction и wheel_handle с типом «функция» исполняет функцию, которая присвоена wheel_handle. Функция же set_handle определяет, над какими блоками какую функцию исполнять. То есть, чуть забежав вперёд, для моего примера конструкция

set_handle("r", set_red);

будет означать, что при наведении мыши на блок с идентификатором «r», переменной wheel_handle присвоится функция set_red, которая с параметром direction будет выполнена в случае события onmousewheel.

Оформляю пример:
<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            #r { position: absolute; left: 10px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height: 100px; background: #000; } 
            #g { position: absolute; left: 120px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height:100px; background: #000; }
            #b { position: absolute; left: 230px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height:100px; background: #000; }
            #result { position: absolute; left: 10px; top: 120px; width: 320px; height: 100px; background: #000; }
        </style>
        
        <script type="text/javascript">
            var wheel_handle = null;
            
            var rgb = {
                r: 0,
                g: 0,
                b: 0,
                result: 0
            }
                    
            var mouse_wheel = function(event) {
                if (false == !!event) event = window.event;
                var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
                if (direction && !!wheel_handle && typeof wheel_handle == "function") {
                    wheel_handle(direction);
                }
            } 
            
            var set_handle = function(id, func) {
                document.getElementById(id).onmouseover = function() {
                    wheel_handle = func;
                };
                document.getElementById(id).onmouseout = function() {
                    wheel_handle = null;
                };
            }
                                    
            var set_red = function(direction) {
                rgb.r += direction;
                if (rgb.r < 0) rgb.r = 0;
                if (rgb.r > 255) rgb.r = 255;
                document.getElementById("r").innerHTML = rgb.r;
                document.getElementById("r").style.backgroundColor = "rgb(" + rgb.r + ", 0, 0)";
                set_result();
            }
            
            var set_green = function(direction) {
                rgb.g += direction;
                if (rgb.g < 0) rgb.g = 0;
                if (rgb.g > 255) rgb.g = 255;
                document.getElementById("g").innerHTML = rgb.g;
                document.getElementById("g").style.backgroundColor = "rgb(0, " + rgb.g + ", 0)";
                set_result();    
            }

            var set_blue = function(direction) {
                rgb.b += direction;
                if (rgb.b < 0) rgb.b = 0;
                if (rgb.b > 255) rgb.b = 255;
                document.getElementById("b").innerHTML = rgb.b;
                document.getElementById("b").style.backgroundColor = "rgb(0, 0, " + rgb.b + ")";    
                set_result();
            }

            var set_result = function() {
                document.getElementById("result").style.backgroundColor = "rgb(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ")";    
            }   
            
            window.onload = function() {
                if (window.addEventListener) window.addEventListener("DOMMouseScroll", mouse_wheel, false);
                window.onmousewheel = document.onmousewheel = mouse_wheel;
                
                set_handle("r", set_red);
                set_handle("g", set_green);
                set_handle("b", set_blue);
            }
        </script>
    </head>
    <body style="height: 1000px;">
        <div id="r">0</div>
        <div id="g">0</div>
        <div id="b">0</div>
        <div id="result"></div>
    </body>
</html>

Финкции вида set_* очевидны и, на мой взгляд в комментариях не нуждаются. А вот обратить внимание на стиль тэга body надо. Я задал 1000 пикселей высоты для того, чтобы страница гарантированно не поместилась в экран и у неё образовалась своя полоса прокрутки. Если запустить пример в таком виде то события onmousewheel на блоках-то работать будут, но при этом и сама страница будет прокручиться вниз. Перечитываю постановку задачи, вношу маленький штришок в функцию mouse_wheel();
var mouse_wheel = function(event) {
    if (false == !!event) event = window.event;
    var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
    if (direction && !!wheel_handle && typeof wheel_handle == "function") {
        if (event.preventDefault) event.preventDefault();
        event.returnValue = false;
        wheel_handle(direction);
    }
} 

preventDefault() нужен здесь для отмены события. Да и вообще он нужен для отмены события. Вобщем, процитирую я немного Гугла:
Если действие события можно отменить, метод preventDefault() объекта Event используется для отмены события. Это означает, что действие, выполняемое в конкретной реализации, при возникновении данного события выполняться не будет. Если на любой фазе развития события вызывается метод preventDefault(), событие отменяется. Действие, выполняемое этим событием по умолчанию, не выполняется. Вызов данного метода для события, которое нельзя отменить, не оказывает никакого влияния на дальнейшее выполнение события. После вызова метод preventDefault() будет действовать на всем протяжении дальнейшего распространения события. Метод preventDefault() можно вызывать на любой фазе потока события.


event.returnValue — это для IE, который о preventDefault() не знает.

Отменили естественную реакцию на событие — выполняем нашу функцию. Под Windows в восьмом Firefox я отметил не всегда корректное срабатывание preventDefault(). Хотя, может это и некоторая специфика onmouseenter, потому что было замечено, что некорректные срабатывания случаются, когда курсор находится на текстом в блоке. Если кто-нибудь в курсе этих катаклизмов, просветите, пожалуйста — здесь Гугл не помог.

Но, вобщем-то и всё. Исходный код для копипасты полностью:
<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            #r { position: absolute; left: 10px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height: 100px; background: #000; } 
            #g { position: absolute; left: 120px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height:100px; background: #000; }
            #b { position: absolute; left: 230px; top: 10px; width: 100px; height: 100px; font-size: 50px; color: #fff; text-align: center; line-height:100px; background: #000; }
            
            #result { position: absolute; left: 10px; top: 120px; width: 320px; height: 100px; background: #000; }
        </style>
        
        <script type="text/javascript">
            var wheel_handle = null;
            
            var rgb = {
                r: 0,
                g: 0,
                b: 0,
                result: 0
            }
                    
            var mouse_wheel = function(event) {
                if (false == !!event) event = window.event;
                var direction = ((event.wheelDelta) ? event.wheelDelta/120 : event.detail/-3) || false;
                if (direction && !!wheel_handle && typeof wheel_handle == "function") {
                    if (event.preventDefault) event.preventDefault();
                    event.returnValue = false;
                    wheel_handle(direction);
                }
            } 
            
            var set_handle = function(id, func) {
                document.getElementById(id).onmouseover = function() {
                    wheel_handle = func;
                }
                document.getElementById(id).onmouseout = function() {
                    wheel_handle = null;
                }
            }
                                    
            var set_red = function(direction) {
                rgb.r += direction;
                if (rgb.r < 0) rgb.r = 0;
                if (rgb.r > 255) rgb.r = 255;
                document.getElementById("r").innerHTML = rgb.r;
                document.getElementById("r").style.backgroundColor = "rgb(" + rgb.r + ", 0, 0)";
                set_result();
            }
            
            var set_green = function(direction) {
                rgb.g += direction;
                if (rgb.g < 0) rgb.g = 0;
                if (rgb.g > 255) rgb.g = 255;
                document.getElementById("g").innerHTML = rgb.g;
                document.getElementById("g").style.backgroundColor = "rgb(0, " + rgb.g + ", 0)";
                set_result();    
            }

            var set_blue = function(direction) {
                rgb.b += direction;
                if (rgb.b < 0) rgb.b = 0;
                if (rgb.b > 255) rgb.b = 255;
                document.getElementById("b").innerHTML = rgb.b;
                document.getElementById("b").style.backgroundColor = "rgb(0, 0, " + rgb.b + ")";    
                set_result();
            }

            var set_result = function() {
                document.getElementById("result").style.backgroundColor = "rgb(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ")";    
            }   
            
            window.onload = function() {
                if (window.addEventListener) window.addEventListener("DOMMouseScroll", mouse_wheel, false);
                window.onmousewheel = document.onmousewheel = mouse_wheel;
                
                set_handle("r", set_red);
                set_handle("g", set_green);
                set_handle("b", set_blue);
            }
        </script>
    </head>
    <body style="height: 1000px;">
        <div id="r">0</div>
        <div id="g">0</div>
        <div id="b">0</div>
        <div id="result"></div>
    </body>
</html>


Ссылка на рабочий пример

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

Я не знаю лучшего средства для для профилактики всяких простудных неприятностей, чем плошка горячего, ароматного лукового супа руанским манером, и готовлю я его так:
  • ставлю в духовку минут на 50—60 всё необходимое для бульона: пару красных луковиц, большую морковь, корень сельдерея, головку чеснока и, конечно, мясо — кусок хорошей говядины и пару-тройку крупных костей;
  • режу кольцами лук, обычный репчатый или шалот — довольно много, грамм 800, килограмм — и кладу его в разогретую толстую чугунную кастрюлю или казан, в котором предварительно растапливаю грамм 150—200 сливочного масла, убавляю огонь до минимума и оставляю пассероваться часа три, периодически помешивая;
  • варю бульон — плотный консоме, залив всё, что запекалось в духовке двумя литрами холодной (!) воды и поставив на минимально возможный огонь. Кипеть практически не должно: два-три пузырька в центре — и хватит. Если кипит сильнее, зачёрпываю половником и выливаю обратно с высоты;
  • когда (часа через три) лук станет почти однородной массой и приобретёт цвет молочного шоколада, я вливаю стакан белого вина, довожу до кипения и вливаю приготовленный и процеженый бульон. Так же, на этом этапе можно добавить грамм сто мягкого сливочного сыра, но это не обязательно;
  • минут через тридцать я добавляю двести грамм сливок, предварительно влив в суп разведённую в полустакане воды ложку муки, тщательно перемешиваю: суп должен быть практически однородным (если есть сомнения можно довести до ума погружным блендером), добавляю молотый перец и травы, которым вариться нужно совсем немного — чабрец, розмарин, лавровый лист или весь букет Гарни скопом;
  • ещё минут двадцать и суп готов: вынимаю лавровый лист и выбрасываю, а остальное подаю в керамических или глиняных глубоких тарелках с гренками и тонко нарезаным сычужным сыром. Гренки — в тарелку, сыр — отдельно.


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

Не болейте!