Posted by Pavel Podlipensky on February 06 10:20 AM

-webkit-box-shadow (в последнем Webkit) и -moz-box-shadow поддерживают "внутренние" тени вместе с inset командой. Также имеется возможность указать несколько теней через зяпятую.

Чтобы получить такую картинку, достаточно написать следующее:

   1: div.box {
   2:    -webkit-box-shadow: 0 3px 3px rgba(0,0,0,0.20), rgba(0,0,0,0.12) 0px 0px 10px inset;
   3:    -moz-box-shadow: 0 3px 3px rgba(0,0,0,0.20), rgba(0,0,0,0.12) 0px 0px 10px inset;
   4:    -webkit-border-radius: 10px;
   5:    -moz-border-radius: 10px;
   6:    border: 1px solid #fff;
   7:    padding: 6px;
   8:    width: 200px;
   9:    background: #fff;
  10: }
Posted by Pavel Podlipensky on October 21 5:31 AM

 

 

Согласно опросу проведенными Money Magazine и PayScale.com, профессия разработчика программного обеспечения 4ая в списке наиболее спокойных профессий. На первом месте профессия Education/Training Consultant, на втором Physical Therapist, третьем - College Professor. В качестве аргументов, в пользу такого положения нашей профессии в рейтинге, приводилось следующее:

1. Свободный график работы.

2. Возможность работать издому.

3. Гибкость сроков выполнения небольших подзадач.

Но что особенно приятно - технические писатели, админы и даже архитекторы теряют больше нервных клеток, чем простые девелоперы.

Берегите нервы, товарищи девелоперы и приятного вам кризиса!

Posted by Pavel Podlipensky on September 07 8:08 PM
Рано или поздно наступает момент, когда возможностей фреймворка вам недостаточно. "Ах, как же так, забыли такую полезную кнопочку/контрол/приложение сделать..." - сетуете вы. Вот тогда и приходит в голову идея написать нехватающий функционал самому. Точнее дописать. И любой уважающий себя фреймворк предоставляет механизмы для собственного расширения/изменения. ExtJs не стал исключением.

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

   1: SquarePanel = Ext.extend(Ext.Panel, {
   2:     //Устанавливаем размер панели по умолчанию
   3:     width: 100,
   4:     height: 100,
   5:     onResize: function(width, height) {
   6:         //Если размеры нашей панели были изменены, причем непропорционально - вернуть ей квадратную форму
   7:         if (width != height) {
   8:             this.body.setWidth(height);
   9:         }
  10:         //Вызываем базовый метод класса-родителя, т.е. - класса Panel.
  11:         SquarePanel.superclass.onResize.call(this, height, height);
  12:     },
  13:     //Добавляем метод который позволит нам изменять размеры нашей панели, указав лишь размер одной ее стороны
  14:     setSquareSize(length){
  15:         this.body.setWidth(length);
  16:         this.body.setHeight(length);
  17:     }
  18: });

Вот такая нехитрая конструкция Ext.extend позволяет создать новый класс, с дополнительными возможностями, а именно – новое поведение панели при изменении ее размеров и новый метод setSquareSize позволяющий установить новые размеры панели. Я акцентировал внимание на дополнительных возможностях, потому что все свойства, методы и события базового класса (Ext.Panel) теперь присущи и нашему классу. Если заглянуть внутрь реализации метода extend, то можно увидеть, что

   1: //создаем пустую функцию-класс F
   2: var F = function(){},
   3:     //сюда будет записана ссылка на прототип дочернего класса (т.е. нашего собственного)
   4:     sbp, 
   5:     //сюда будет записана ссылка на прототип родительского класса
   6:     spp = sp.prototype;
   7: //прототипом класса F становится прототип родительского класса
   8: F.prototype = spp;
   9: //прототипом нашего же класса становится класс F
  10: sbp = sb.prototype = new F();
  11: //далее идет переопределение конструктора и назначение суперкласса
  12: sbp.constructor=sb;
  13: sb.superclass=spp;
  14: //...много непонятных буков

Методы и свойства, которые мы переопределили (onResize, width и height) попадут в prototype нашего класса вот таким образом:

   1: //sb - это наш класс SquarePanel, а overrrides - объект содержащий дополнения к нашему классу
   2: Ext.override(sb, overrides);

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

Далее мы можем расширять нашу панель точно также как и Ext.Panel

   1: DifferentSquarePanel = Ext.extend(SquarePanel, {/* some additional logic here */})

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

Posted by Pavel Podlipensky on July 29 7:00 AM

Сегодня я начинаю цикл статей посвященных фреймворку ExtJs. Это не будут туториалы или мануалы для пошагового изучения библиотеки. Я просто хочу поделиться своим опытом, поэтому начну с чего попало, а именно – с событий.

С изобретением GUI и Мыши, DOM-объекты, подобные кнопкам и текстовым полям стали обладать некими событиями. Например, click, mouseover, keydown и другими. По сути событие – это сообщение, вызов функции, генерируемое одной частью приложения (источником), которое извещает другую часть приложения (приемник или подписчик) о том, что что-то случилось.

   1: <div id="theDiv" onclick="alert('You clicked me')">Click me!</div>

В ExtJs DOM-элементы оборачиваются в Ext.Element, поэтому подписка на тоже событие будет выглядеть так:

   1: Ext.get('theDiv').on('click', function() {alert('You clicked me');});

При разработке сложных пользовательских интерфейсов, отдельные элементы DOMа получают особое назначение. К примеру, банальный div может быть заголовком колонки в таблице (я имею ввиду ExtJs Grid). Следовательно, когда пользователь захочет изменить порядок колонок в гриде и начнет перетягивать колонку за заголовок – в заголовке произойдут некие события (mouseover, mousedown, click, …). Каждый раз отслеживать порядок и детали “низкоуровневых” событий в своем коде рутинно. Поэтому ExtJs поддерживает собственную модель событий. К примеру, GridPanel поддерживает такие события как columnmove, cellclick, headerclick и другие.

Вся магия событий в ExtJs реализована в классе Ext.util.Observable. Чтобы наделить ваш компонент свойствами источника события, вам необходимо “унаследоваться” от Ext.util.Observable.  Если же вы наследуете свой компонент от других компонентов, таких как Panel, Grid, Form, Tree и другие, то дополнительно наследоваться от Ext.util.Observable не обязательно. О принципе паттерна Observable и об особенностях его реализации на javascript можно прочесть на CodeProject.

После того как вы так или иначе унаследовались от Ext.util.Observable, вы можете добавить ваши собственные события примерно таким образом

   1: this.addEvents( 'IAmHappy', 'IAmNotHappy' );

Где, this – это и есть ваш компонент.

Инициировать ваше событие также просто:

   1: this.fireEvent('IAmHappy', this, firstArg, secondArg);

Когда мы вызываем функцию fireEvent, то Observable смотрит, подписывался ли на это событие (в данном случае – ‘IAmHappy’) кто-либо или нет. Если подписывался, то вызывает функцию-обработчик и устанавливает значение переменной this равное текущему объекту (этого мы добились указав this вторым параметром после названия события).

Также в эту функцию будут переданы параметры event и target. Где, event будет объект типа Ext.EventObject, а target – ссылка на HTMLElement. Следом за этими параметрами пойдут наши, собственные параметры firstArg и secondArg, коих может быть любое количество.

   1: function IAmHappyListener(event, target, fristArg, secondArg){
   2: ...
   3: }

Если же Observable не нашел ни одного подписчика, то он, соответственно, ничего и не вызывает.

Простейший способ подписаться на события ExtJs я показал выше, но существуют также некоторые вариации:

Обработчик событий с задержкой (обработчик запуститься через 250 миллисекунд после события)

   1: el.on('click', this.onClick, this, {delay: 250});

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

   1: el.on('click', this.onClick, this, {buffer: 100});

Обработчик событий с условием распространения события

   1: // prevent default
   2: el.on('click', this.onClick, this, {preventDefault: true});
   3:  
   4: // only stop propagation
   5: el.on('click', this.onClick, this, {stopPropagation: true});
   6:  
   7: // prevent default and stop propagation
   8: el.on('click', this.onClick, this, {stopEvent: true});

preventDefault и stopPropagation означают тоже самое, что и соответствующие методы родных, джаваскриптовых событий.

Обработчик событий с другими опциями

   1: el.on('click', this.onClick, this, {
   2:     single: true, // удаляет автоматически, после первого запуска
   3:     delegate: 'li.some-class' // автоматическая делегация событий
   4: });

Добавление сразу нескольких обработчиков может выглядеть вот так:

   1: el.on({
   2:     'click' : {
   3:         fn: this.onClick,
   4:     scope: this,
   5:     delay: 100
   6:     }, 
   7:     'mouseover' : {
   8:         fn: this.onMouseOver,
   9:     scope: this
  10:     },
  11:     'mouseout' : {
  12:         fn: this.onMouseOut,
  13:         scope: this
  14:     }
  15: });

Еще стоит упомянуть о такой полезной функции как relayEvents. Она позволяет делегировать события одного компонента – другому компоненту. К примеру, у вас есть панель A, содержащая другую панель B, а та в свою очередь содержит кнопку Save. По нажатию на кнопку Save произойдет ваше собственное событие onSomeSave. К сожалению, ни панель А, ни панель B, обработать это событие не может. Поэтому панели А необходимо генерировать это событие как свое собственное, в надежде, что кто-то сможет его обработать.

clip_image001

“Передать” события панели B в панель A и позволяет relayEvents:

   1: panelA.relayEvents(panelB, ['onSomeSave']);

Фух, что-то длинный пост получился. Устал. А я ведь еще не рассказал об Ext.EventManager, о такой полезной функции как within и о многом другом. Ну чтож, это в другой раз.

Полезные материалы.

Полезные ссылки

Document Object Model Events

Introduction to Events

Tutorial:Events Explained

Tutorial:RelayEvents

Posted by Pavel Podlipensky on July 23 2:58 PM

Объявили посадку. Через несколько минут самолет Киев-Нью Йорк приземлился в аэропорту JFK. Большинство в самолете наши. Мы все незнакомы. Но нас объединяет одно - радостные лица.

Так примерно и начиналось мое путешествие в Штаты несколько месяцев  назад. Но написать об этом, я нашел время только сейчас. DSC04408

Потом был еще один самолет и так я оказался в San Francisco, California. Первое впечатление об Америке было на само деле отрицательным - дело все в разнице менталитета. Я чувствовал себя инопланетянином.  Но не потому что был удивлен высотой небоскребов или чистотой улиц... Американцы - вот, что меня удивило с первой минуты. Банальная улыбка, многим ее иногда так не хватает, но когда тебе улыбается несколько сотен человек практически одновременно, где-нибудь на Бродвее или 5й, 7й, да и любой другой авеню. Действительно неприятно. Не знаешь, что делать в ответ. Тоже улыбаться? Я не могу улыбаться в течение суток. У нас - советских людей (да, я рожден в СССР!) - развиты не те мышцы лица. Я пробовал. К вечеру улыбку заклинивает, лицо перекособочивает, получается улыбка смертельно ране нного человека.

Океан. Следующее, что произвело на меня сильное впечатление. На ближайшие выходные я добрался до океана. По случаю июля месяца, никто не купался, только баловались несколько серфингистов в соответствующих костюмах. И мне популярно объяснили, что вдоль всего тихоокеанского побережья Северной Америки идет холодное течение, поэтому температура воды не поднимается выше 18-23 C. Единственные, кого не смущала столь низкая температура воды, были - дети. Они купались и вели себя также как и у нас, на пляжах Крыма - чего-то рыли, что-то искали в прибое, куда-то носили ведёрками воду и строили немыслимые строения из песка.P7040021

Американы говорят: "Америку сделал автомобиль". Это понятно. Большую часть жизни они проводят в авто. Машины хороши, нет слов; но не менее хороши дороги, по которым эти машины ездят. Бетонные плиты, уложенные стык в стык, почти без трещин. Некоторые участки дорог – платные (в частности мосты). Что есть платная дорога? Это кратчайший путь к вашей цели, с прекрасным скоростным режимом и пропускной способностью (за счет качества покрытия). Пробки есть, но с ними активно борятся – появились так называемые Car pool. Это полосы по которым можно ехать если в машине 2 и более человек. А если кто-то сломался, его тут же стаскивают с дороги (есть служба специально для этого). При въезде на платную дорогу вы даете кредитку и въезжаете на дорогу (на некоторых участках просто фоторафируют ваш номер и присылают раз в месяц счет за проезд). Занимает секунды. Как я узнал позже, номера имеют не только улицы, но и дороги, и вся Америка разбита на некие сектора под номерами. Если вы хотите уйти от полиции, это будет сделать непросто. Стукачей полно, и все сектора (дороги) под номерами. Даже сами американцы имеют свои номера. Это называется номер социального страхования. Вся их жизнь - это сплошное громадье цифр, номеров, счетов. Заблудиться почти невозможно. Останавливаетесь, смотрите карту, номер сектора дороги, где вы сейчас, и ориентируетесь моментально.

Как вы понимаете, в Штатах я оказался не отдыха ради, а по работе. О деталях проекта, по понятным причинам, я рассказать ничего не могу. Скажу лишь, что в ближайшее время вы сможете увидеть посты на тему Enterprise Patterns, различных особенностей SOA архитектур, пару слов об ExtJs, а также о войнах браузеров. Часто писать не обещаю – много работы, а релиз как всегда запланирован на вчера ;)DSC04403

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

P.S.

Теперь ваши комментарии будут появляться в блоге, лишь спустя 12 часов – апрувить я смогу их лишь своим ранним утром. Не обессудьте.

Posted by Pavel Podlipensky on April 27 8:30 PM

Вы бородатый веб разработчик и думаете уже видали всякие чудеса в JavaScript-коде? Хм, чтож тогда следующие задачки для вас. 

1. ++Math.PI
2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3)
3. typeof NaN
4. typeof typeof undefined
5. a = {null:null}; typeof a.null;
6. a = "5"; b = "2"; c = a * b;
7. a = "5"; b = 2; c = a+++b;
8. isNaN(1/null)
9. (16).toString(16)
10. 016 * 2
11. ~null
12. "ab c".match(/\b\w\b/)

Жду ваши варианты в комментариях (просьба не постить по десять одинаковых комментариев, все равно раньше чем я их промодерю они не появятся). До компа я доберусь только завтра, поэтому и ваши комментарии тоже появятся только завтра, а заодно и обновление к этому посту - в виде ответов и объяснений к решениям (да, я их сопру с ваших комментариев, поэтому сначала думайте, а потом пишите!) 

Ответы и решения:

1. 4.141592653589793

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

2. false

Такой же ответ получат и Java разработчики, потому как и Java и JavaScript используют IEEE 754 числа с плавающей точкой. Более подробно об операциях с числами с плавающей точкой можно найти тутаньки 

3. "number"

Логического объяснения этому я не нашел, единственное что могу посоветовать, это стараться использовать isNaN вместо typeof SomePotentialNumber, во избежание казусов. 

4. "string" 

Тут все просто - первый оператор typeof вернет тип переменной в виде строки, следовательно второй оператор должен вернуть тип результата первого оператора, т.е. string. 

5. "object"

Мы присвоили переменной а, объект с полем "null", значение которого null, а это в свою очередь, пусть и специфический, но объект. Другими словами мы тут имеем a.null === null.

6. 10

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

7. 7

Порядок выполнения операндов в Javascript  идет слева направо, поэтому мы имеем "а увеличить на 1, а затем сложить с b". 

8. false

С этим тоже, думаю, все понятно. 

9. 10

toString() принимает числовой параметр (необязательный) и toString(16) означает, что число записано в шестнастеричной системе счисления, посему ответ и 10. 

10. 28

016 это восьмеричное представление числа 14 в десятеричной системе счисления. 

11. -1 


12. [ "c" ]

Отдельно стоящий символ в той строке только один - "с", это и ищет указанное регулярное выражение. 

Posted by Pavel Podlipensky on April 26 3:34 PM

Думаю все уже в курсе, что у нас кризис (теперь кажется финансовый), впрочем как и у всех. И каждый понимает что ничего хорошего ему это событие не принесет. А одним из "благ" кризиса является минимальная зарплата, т.е. та сумма денег, за которую и вы не против поработать и шефу увольнять вас пока нет повода. Звучит неплохо, правда? Неправда. У большинства людей хорошо развито чувство собственничества, поэтому пока это не мое, мне все равно кто этим владеет, но как только оно стало моим - хрен кто это у меня отберет! А тут такое дело - зарплату понижают! Не буду больше растекаться по древу вот вам поучительный комикс на эту тему: