Март 2011

Руководство по actionscript. часть 2, стр. 070

Приемник из файла Module. swf регистрируется в объекте файла Main. swf

Предположим, что SWF-файл, находящийся на одном сайте (site-a. com/Main. swf), загружает SWF-файл, расположенный на другом сайте (site-b. com/Module. swf). Предположим также, что в файле Module. swf определен приемник, который желает зарегистрироваться в объекте, созданном в файле Main. swf. Чтобы разрешить данную регистрацию, перед регистрацией приемника из файла Modul е. swf в файле Main. swf должна быть выполнена следующая строка кода:

Security. а11owDoma i n(«s i tе-b. com»);

Эта строка позволяет всем SWF-файлам, находящимся на сайте site-b. com (включая файл Module. swf), регистрировать приемники в любом объекте, созданном в файле Main. swf.

Приемник из файла Main. swf получает уведомление

о событии, получателем которого является отображаемый

объект в файле Module. swf

Продолжая рассматривать сценарий, в котором файл Main. swf загружает файл Module. swf из предыдущего раздела, предположим, что экземпляр основного класса файла Main. swf добавляет объект класса Loader, содержащий файл Module. swf, в свою иерархию отображения, как показано в следующем коде:

package { import flash. display.*; import flash. net.*; import flash. events.*;

public class Main extends Sprite { private var loader:Loader;

public function Main( ) { loader = new Loader( );

1oader.1oad(new URLRequest(«http://site-b. com/Module. swf»));

// Добавляем объект Loader, содержащий файл Module. swf, в иерархию

// отображения данного объекта

addChild(loader);

• } .} }.

Руководство по actionscript. часть 2, стр. 071

Предположим также, что экземпляр основного класса файла Main. swf желает получать уведомления всякий раз, когда пользователь щелкает кнопкой мыши на объекте из файла Module. swf. Следовательно, экземпляр основного класса файла Main, swf регистрирует приемник в объекте loader для событий MouseEvent. CLICK, как показано в следующем коде:

package { import flash. display.*: import flash. net.*; import flash. events.*;

public class Main extends Sprite { private var loader:Loader;

public function Main( ) { loader = new Loader( );

1 oader.-load(new URLRequest(«http://site-b. com/Module. swf»)); addChild(loader);

1oader. addEventLi stener(MouseEvent. CLICK. clickListener); } •

private function clickListener (e:MouseEvent):void { trace(«Module. swf was clicked»);

}

}

}

Тем не менее, поскольку файлы Ma in. swf и Module, swf размещены в различных интернет-доменах, ограничения безопасности запрещают вызывать метод clickListener ( ) для возникающих событий MouseEvent. CLICK, получателями которых являются отображаемые потомки объекта loader (то есть отображаемые объекты из файла Module. swf).

Для того чтобы обойти данное ограничение, конструктор основного класса файла Module. swf содержит следующую строку кода:

Security. al1owDoma i n(«s i te-a. com»);

После выполнения этой строки файл Module. swf начнет доверять файлу Main. swf (и всем SWF-файлам с сайта site-a. com), благодаря чему экземпляр основного класса файла Ma in. swf будет включен клиентской средой выполнения Flash в цепочку диспетчеризации события MouseEvent. CLICK, получателем которого являются объекты из файла Module. swf. В результате метод clickListener ( )

будет вызываться всякий раз при щелчке кнопкой мыши на объекте из файла Module. swf.

Руководство по actionscript. часть 2, стр. 072

Подробную информацию о. методе allowDomain() и безопасности приложения Flash Player м$ j * можно найти в разд. «Разрешения создателя (allowDomain())» гл. 19.

Стоит отметить, что вызов метода а 11 о wDoma i n ( ) позволяет не только обрабатывать события между границами зон безопасности: все SWF-файлы из разрешенного домена получают возможность осуществлять кросс-скриптинг над SWF-файлом, в котором был вызван метод allowDomain ( ). Однако существует альтернатива всеобъемлющим разрешениям, выдаваемым методом allowDomain ( ).

Альтернатива методу allowDomain( ): разделяемые события

В некоторых случаях SWF-файлы из различных доменов могут пожелать совместно использовать события, не предоставляя при этом всех полномочий для кросс-скриптинга. Для решения подобных проблем приложение Flash Player предоставляет переменную экземпляра sharedEvents класса Loaderlnf о. Переменная sharedEvents — это простой нейтральный объект, через который два SWF-файла могут отправлять события друг другу, независимо от ограничений, обусловленных требованиями безопасности. Этот подход позволяет осуществлять взаимодействие между SWF-файлами, основанное на событиях, не отменяя требований безопасности, но для его реализации требуется написание большего объема кода, чем при использовании альтернативного подхода с методом allowDomain ( ).

Рассмотрим применение переменной sharedEvents на примере. Предположим, что Томми основал компанию по производству фейерверков и создал рекламный сайт www. blast. ca с использованием технологии Flash. Томми нанял подрядчика Дерека для создания отдельного элемента, реализующего эффект, который заключается в хаотичной генерации анимированных взрывов фейерверков под указателем мыши. Дерек создает SWF-файл MouseEf fect. swf, в котором реализован этот эффект, и размещает его по адресу www. dereksflasheffects. com/MouseEffect. swf. Дерек говорит Гомми загрузить файл MouseEf f ect. swf в его приложение www. blast. ca/BlastSite. swf. Церек и Томми согласились, что файл MouseEf feet. swf должен размещаться на :ервере www. derekflasheffects. com, что в дальнейшем позволит Дереку легко обновлять данный файл, не внося при этом никаких изменений в сайт Томми.

Гомми просит Дерека изменить файл MouseEf feet. swf таким образом, чтобы генерация взрывов прекращалась в тот момент, когда указатель мыши покидает об-1асть отображения приложения Flash Player. Дерек считает эту идею целесообраз-¦юй и приступает к написанию соответствующего кода. В обычной ситуации, чтобы шределить выход указателя мыши за пределы области отображения приложения Flash Player, код в файле MouseEf feet. swf должен зарегистрировать приемник * экземпляре Stage для событий Event. MOUSE_LEAVE. Однако, поскольку фай-ты MouseEf feet. swf и BlastSite. swf размещены в разных доменах, файл MouseEf feet. swf не имеет доступа к экземпляру Stage. Томми решает, что

вместо того, чтобы предоставлять файлу MouseEf feet. swf полный доступ к файлу BlastSite. swf, он просто переадресует все события Event. MOUSE_LEAVE файлу MouseEf feet. swf через переменную sharedEvents.

Листинг 12.6 демонстрирует код файла BlastSite. swf, относящийся к переадресации событий.

Листинг 12.6. Переадресация события через переменную sharedEvents

package { import flash. display.*; import flash. net.*; import flash. events.*; import flash. system.*;

public class BlastSite extends Sprite { private var loader:Loader;

public function BlastSite ( ) { // Загружаем файл MouseEffeet. swf loader = new Loader( ); loader. load(

new URLRequest(«http://www. dereksf1asheffects. com/MouseEffect. swf»)); addChild(loader);

// Регистрируем приемник для событий Event. MOUSE_LEAVE

stage. addEventLi stener(Event. MOUSE_LEAVE. mouseLeaveLi stener);

}

// Когда возникает событие Event. MOUSELEAVE…. private function mouseLeaveListener (e:Event):void {

// …переадресуем его файлу MouseEffeet. swf

1oader. contentLoaderlnfо. sharedEvents. di spatchEvent(e);

}

}

}

Листинг 12.7 демонстрирует код файла MouseEf feet. swf, относящийся к обра ботке событий.

Руководство по actionscript. часть 2, стр. 073

Листинг 12.7. Обработка события, полученного через переменную sharedEvents

package { import flash. display. Sprite; import flash. events.*;

public class MouseEffeet extends Sprite { public function MouseEffeet ( ) { // Регистрируем приемник для событий Event. MOUSELEAVE. получаемых // через переменную sharedEvents

1 oaderInfo. sharedEvents. addEventLi stener(Event. MOUSE LEAVE.

mouseLeaveListener);

// Обрабатываем события Event. MOUSE_LEAVE. полученные через переменную // sharedEvents

private function mouseLeaveListener (e:Event):void { traceCMouseEffect. mouseLeaveLi stener( ) was invoked…»); // Здесь прекращаем генерацию взрывов…

}

}

}

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

Что дальше?

Мы достигли больших успехов в изучении основ языка ActionScript. Если вы поняли концепцию прочитанных 12 глав, то теперь обладаете достаточными знаниями языка ActionScript, чтобы приступить к рассмотрению большей части API клиентской среды выполнения Flash. Таким образом, пришло время сделать собственный выбор. Если вы желаете продолжить знакомство с базовыми возможностями ActionScript, переходите к чтению гл. 13, в которой будет рассказано, как создавать код, позволяющий выходить из сбойных ситуаций на этапе выполнения программы. Если, с другой стороны, вы предпочитаете узнать, как использовать язык ActionScript для отображения содержимого на экране, сразу переходите к части И.

Руководство по actionscript. часть 2, стр. 074

Пиши

Обработка исключений

и ошибок

В этой главе мы познакомимся с системой языка ActionScript, предназначенной для генерации и реагирования на ошибки этапа выполнения программы, которые называются исключениями. В языке ActionScript ошибки могут генерироваться как средой Flash, так и выполняемой программой. Ошибки, генерируемые средой Flash, называются предопределенными; ошибки, генерируемые программой, называются пользовательскими. В программе можно реагировать на любые ошибки (как предопределенные, так и пользовательские), или, иначе говоря, обрабатывать их с помощью инструкции try/catch/finally. Генерация ошибок осуществляется с помощью инструкции throw.

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

I Изменения, которые будут внесены в программу «Зоопарк» на протяжении этой главы, м?; j „ являются последними для этой программы до конца книги. Чтобы завершить программу по созданию виртуального зоопарка, мы должны рассмотреть вопросы, связанные с экранным программированием и работой с мышью, которые освещены в части П. Прочитав часть II, обратитесь к приложению, чтобы узнать, как добавить графику и интерактивность в программу «Зоопарк».

Механизм обработки исключений

Если помните, класс VirtualPet определяет метод setName ( ), который присваивает значение переменной petName экземпляров класса VirtualPet. Чтобы освежить вашу память, ниже представлен соответствующий код класса VirtualPet (те части класса, которые не имеют отношения к присваиванию значения переменной petName, не приводятся):

public class VirtualPet { private var petName:String;

public function setName (newName:String):void { // Если длина заданного нового имени больше maxNameLength символов… if (newName. length > Virtual Pet. maxNameLength) { // …обрезать имя

newName = newName. substr(0, VirtualPet. maxNameLength); } else if (newName == «») { // …в противном случае, если заданное новое имя является // пустой строкой, завершить выполнение метода, не изменяя // значения переменной petName

return;

}

// Присвоить новое проверенное имя переменной petName petName = newName;

}

}

Метод setName ( ) перед тем, как изменить значение переменной petName, проверяет, является ли допустимым количество символов в новом имени животного. Если новое имя животного не является допустимым, значение переменной не изменяется; в противном случае изменение значения допускается.

Руководство по actionscript. часть 2, стр. 075

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

Для генерации исключения в нашем коде мы используем оператор throw, который имеет следующий вид:

throw выражение

В предыдущем коде выражение — это значение данных, описывающее некоторую необычную или проблематичную ситуацию. Использование оператора throw для оповещения об ошибке иногда называется «генерацией исключения». Язык ActionScript позволяет использовать любое значение в качестве выражения выражение в операторе throw. Например, значением выражения выражение может быть строковый литерал «Something went wrong!11 (Что-то не получилось!) или числовой код ошибки. Однако корпорация Adobe рекомендует использовать в качестве значения выражения выражение экземпляр предопределенного класса Error (или одного из его подклассов), считая этот подход хорошей практикой. Класс Error является стандартным классом, представляющим исключительные ситуации в программе. Его переменная экземпляра message используется для описания ошибки.

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

public function setName (newName:String):void { // Если длина заданного нового имени больше maxNameLength символов… if (newName. length > Virtual Pet. maxNameLength || newName = «») { // …генерируем ошибку

throw new Error(«Invalid pet name specified.»);

}

// Присвоить новое допустимое имя переменной petName petName = newName;

В нашей новой версии метода setName ( ), если значение параметра newName является недопустимым, мы используем оператор throw для прекращения выполнения метода вместо того, чтобы просто обрезать указанное имя, как мы делали раньше. Кроме того, мы указываем описание проблемы — «Invalid pet name specified» (Указано недопустимое имя животного) — в качестве аргумента конструктора Error. Это описание определяет ситуацию, из-за которой возникла ошибка. Конструктор Error присваивает это описание переменной message созданного объекта Error.

Если метод setName ( ) не обнаружит никаких проблем со значением параметра newName, то он завершится нормально, и код, вызвавший его, может быть уверен, что работа, возложенная на этот метод, выполнена успешно. В противном случае блок catch должен обработать возникшую проблему. Блок catch является частью большой инструкции, называемой инструкцией try/catch/finally. Инструкция try/catch/ fin ally предусматривает план восстановления для кода, который может сгенерировать исключение. Вот общая структура типовой инструкции try/ catch/finally:

try {

// Код в этом блоке может генерировать исключения } catch (е:тип) {

II Код в этом блоке обрабатывает возникшую проблему } finally {

// Код в этом блоке выполняется всегда, независимо от того, // сгенерировал блок try исключение или нет

}

В приведенном коде ключевое слово try сообщает среде Flash, что мы собираемся выполнить код, который может сгенерировать исключение. Блок catch обрабатывает исключения, генерируемые блоком try. Код в блоке catch выполняется в том, и только в том случае, когда код в блоке try сгенерировал исключение. Код в блоке final 1 у выполняется всегда после завершения выполнения блока try или catch. Блок final 1 у инструкции try/ catch/finally обычно содержит очищающий код, который должен выполняться независимо от того, было сгенерировано исключение в соответствующем блоке try или нет.

Руководство по actionscript. часть 2, стр. 076

Обратите внимание на типовую структуру.

1. Блок try выполняет код, который может сгенерировать исключение.

2. Код в блоке try использует оператор throw для оповещения о любых ошибках.

3. Если в блоке try не возникло никаких ошибок, то он выполняется полностью и программа пропускает блок catch.

4. Если в блоке try была сгенерирована ошибка, то его выполнение прекращается и начинается выполнение блока catch. Блок catch способен обрабатывать любые ошибки, возникающие в блоке try.

5. Выполняется блок final 1 у.

В большинстве случаев блок finally не требуется и, следовательно, опускается. В последующих примерах мы будем опускать блок finally. Далее, в разд. «Блок finally», мы рассмотрим пример использования этого блока.

Когда выполняется блок catch, он получает значение выражения выражение оператора throw в качестве параметра. В блоке catch это значение может помочь выявить ошибку, сгенерированную в блоке try. Образно говоря, код, в котором возникла проблема, бросает (throw) исключение (передает объект Error) в блок catch, который получает этот объект в качестве параметра (ловит (catch) его).

I Далее в разд. «Передача исключений вверх по иерархии объектов» мы выясним, что м$ 4 щ произойдет в том случае, если возникшая ошибка не будет обработана. —За*-

Рассмотрим пример инструкции try/catch/finally: try {

somePet. setName(«James»);

// Если мы находимся здесь, значит, исключение не возникло; // продолжаем выполнение, как планировалось ранее. traceCPet name set successfully.»); } catch (e:Error) { // ОШИБКА! Недопустимые данные. Выводим предупреждение. traceC’An error occurred: » + e. message);

}

Если при вызове метода pet. setName ( ) внутри предыдущего блока try оператор throw метода setName ( ) не будет выполнен (если не произойдет никакой ошибки), то все последующие инструкции в блоке try будут выполнены успешно и программа полностью пропустит блок catch. Однако если метод setName ( ) сгенерирует исключение, программа немедленно прекратит выполнение инструкций в блоке try и перейдет к выполнению блока catch. В блоке catch значением параметра е является объект класса Error, переданный в оператор throw внутри метода setName ( ).

Руководство по actionscript. часть 2, стр. 077

В предыдущем примере код в блоке catch при отладке просто отображает значение переменной message объекта Error. Однако в более сложном приложении блок catch может попытаться восстановить работоспособность программы после ошибки, возможно отобразив окно, которое позволит пользователю указать допустимое имя.

Обработка нескольких типов исключений

Пример исключения из предыдущего раздела был чрезмерно упрощен. Что произойдет, если наш метод генерирует ошибки нескольких типов? Все ошибки будут отправлены в один и тот же блок catch? Что ж, это зависит от разработчика. Конечно, они все могут быть отправлены в один блок catch, однако чаще всего обработка различных типов ошибок выполняется отдельными блоками cat ch — и это является хорошей практикой. Рассмотрим почему.

Предположим, что мы хотим получить набор более детальных сообщений об ошибках в нашем методе setName ( ): одно сообщение для недопустимых данных, одно — для слишком короткого имени, еще одно — для слишком длинного имени.

Тело нашего модифицированного метода setName ( ) могло бы выглядеть следующим образом:

if (newNameлndexOfC «) == 0) {

// Имена не могут начинаться с пробела…

throw new Error(«Invalid pet name specified.»); } else if (newName == «») {

throw new Error(«Pet name too short.»); } else if (newName. length > Virtual Pet. maxNameLength) {

throw new ErrorCPet name too long.»);

}

Чтобы обработать все три возможные сообщения об ошибках, генерируемые в нашем новом методе setName ( ), мы могли бы записать код нашей инструкции try/catch /finally следующим образом:

try {

somePet. setName(«некоеИмяЖивотного»);

// Если мы находимся здесь, значит, исключение не возникло;, продолжаем // выполнение, как планировалось ранее. traceCPet name set successfully.»); } catch (e:Error) { switch (e. message) {

case «Invalid pet name specified.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a valid name.»);

break;

case «Pet name too short.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a longer name.»);

break;

case «Pet name too long.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a shorter name.»);

break;

}

}

Надо признаться, что этот код работает, однако он имеет множество недостатков. Самый первый и наиболее серьезный недостаток состоит в том, что ошибки, отличаются друг от друга только текстом в строке, которая скрыта внутри класса Vi rtual Pet. Всякий раз, когда мы хотим узнать, какие типы ошибок могут возникать в методе setName ( ), мы вынуждены обращаться к коду класса VirtualPet и искать строки сообщений об ошибках. Использование сообщений для идентификации ошибок между различными методами и классами зачастую приводит к появлению ошибок, вызванных человеческим фактором, и затрудняет поддержку нашего кода. Второй недостаток заключается в том, что оператор switch сам по себе сложен для чтения. Это ненамного лучше использования, скажем, числовых кодов ошибок вместо формальных исключений.

Руководство по actionscript. часть 2, стр. 078

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

Рассмотрим общий синтаксис оператора try с несколькими блоками catch: try {

// Код, который может генерировать исключения. } catch (е:Тип0шибки1) {

II Код обработки ошибки с типом Тип0шибки1. } catch (е:Тип0шибки2) {

II Код обработки ошибки с типом Тип0шибки2. } catch (е:ТипОшибкип) {

II Код обработки ошибки с типом ТипОшибкип.

)

Если бы оператор throw в предыдущем блоке try сгенерировал исключение, применив в качестве параметра выражение типа Тип0шибки1, был бы выполнен первый блок catch. Например, следующий код приведет к выполнению первого блока catch:

throw new ТипОшибкиК );

Если бы в оператор throw было передано выражение типа Тип0шибки2, был бы выполнен второй блок catch и т. д. Как уже известно, в языке ActionScript выражение оператора throw может принадлежать любому типу данных. Однако помните, что в большинстве программ исключения представляются только экземплярами класса Error или одного из его подклассов и это является хорошей практикой.

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

Определение степени детализации типов исключений. Следует ли определять подкласс класса Error для каждой исключительной ситуации? Обычно ответ на этот вопрос отрицателен — вам не потребуется такая степень детализации, поскольку во многих случаях несколько исключительных ситуаций могут иметь одинаковый смысл. Если вам не нужно задавать различие между несколькими исключительными ситуациями, то можете объединить эти ситуации в одном пользовательском подклассе класса Error. Например, вы можете определить один подкласс класса Error с именем Inval idlnputExcept ion для решения широкого круга проблем, связанных с вводом данных.

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

Руководство по actionscript. часть 2, стр. 079

Ранее мы генерировали три исключения в методе setName ( ). Все три исключения использовали базовый класс Error. Приведем этот код снова:

if (newName. indexOfC «) == 0) {

// Имена не могут начинаться с пробела…

throw new ЕггогС»Invalid pet name specified.»); } else if (newName == «») {

throw new ErrorCPet name too short.»); } else if (newName. length > VirtualPet. maxNameLength) {

throw new ErrorCPet name too long.»);

}

В данном коде, чтобы провести различие между исключениями класса Virtual Pet и остальными исключениями в нашем приложении, мы использовали переменную message класса Error, которая, как уже известно, делает наши исключения неудобными для использования и может привести к появлению ошибок, вызванных человеческим фактором. Для отличия ошибок, относящихся к классу Virtual Pet, от других ошибок в нашем приложении лучше определить пользовательский подкласс класса Error с именем VirtualPetNameException, как показано в следующем коде:

// Код в файле VirtualPetNameException. as: package zoo {

public class VirtualPetNameException extends Error { public function VirtualPetNameException ( ) { // Передаем сообщение об ошибке в конструктор класса Error, которое // будет присвоено переменной message данного объекта ‘ superCInvalid pet name specified.»);

}

}

}

Теперь, когда у нас появился класс VirtualPetNameException, метод setName ( ) может генерировать свой собственный тип ошибки, как показано в следующем коде:

public function setName (newName:String):void { if (newName. indexOfС «) == 0) {

throw new VirtualPetNameException( ); } else if (newName == «») {

throw new VirtualPetNameException( ); } else if (newName. length > Virtual Pet. maxNameLength) {

throw new VirtualPetNameException( );

}

petName = newName;

}

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

VirtualPet будут отличаться от других ошибок приложения, но и то, насколько эти ошибки будут отличаться друг от друга. У нас есть следующие варианты:

Вариант 1. Использовать один класс для исключительных ситуаций класса VirtualPet

В этом случае мы оставляем предыдущее описание метода s е tName ( ) как есть. Как вскоре станет известно, этот вариант позволяет отличать ошибки класса Vi rtual Ре t от других базовых ошибок в программе, однако мы не сможем отличить между собой три внутренние разновидности ошибок класса Virtual Pet (недопустимые данные, слишком короткое имя животного и слишком длинное имя животного).



Полезные ссылки
Случайные записи
  • 12.03.2011">Руководство по actionscript. часть 3, стр. 132
  • 01.03.2011">Руководство по actionscript. часть 5, стр. 112
  • 17.11.2011">Обзор SandyBridge E
  • 09.03.2011">Руководство по actionscript. часть 4, стр. 049
  • 01.03.2011">Руководство по actionscript. часть 5, стр. 125
  • 20.03.2011">Руководство по actionscript. часть 2, стр. 061
  • 09.06.2011">Выбираем курицу
  • 20.07.2011">Как не обмануться, выбирая CMS (часть 2)
  • 14.11.2011">Палитры в Фотошопе
  • 27.02.2011">Руководство по actionscript. часть 6, стр. 035
  • 06.03.2011">Руководство по actionscript. часть 4, стр. 147
  • 27.07.2011">Флористы – художники от мира цветов
  • 04.07.2012">Windows Phone 8 может обзавестись клавиатурой нового типа
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 001
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 032
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...