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

Обращение к объекту, зарегистрировавшему приемник

В процессе диспетчеризации любого события объект Event, передаваемый в каждый приемник события, определяет переменную currentTarget, содержащую ссылку на объект, в котором зарегистрирован этот приемник события. Это демонстрирует следующий обобщенный код приемника события; он отображает строковое значение (типа String) объекта, в котором зарегистрирован приемник не-кийПриемник( ):

public function некийПриемник (е:НекоеСобытие):void { // Обращение к объекту, в котором зарегистрирован данный приемник события trace(e. currentTarget);

}

Для событий, получателями которых являются неотображаемые объекты, значение переменной экземпляра currentTarget класса Event всегда равняется значению переменной экземпляра target (поскольку приемники всегда регистрируются в получателе события). Например, вернемся к классу FileLoader из листинга 12.1. Если мы сравним значения переменных е. currentTarget и е. target внутри метода completeListener ( ), то увидим, что обе ссылаются на один и тот же объект:

package { import flash. display.*;

import flash. net.*; import flash. events.*;

public class FileLoader extends Sprite { public function FileLoader ( ) { var urlLoader:URLLoader = new URLLoader( ); urlLoader. addEventLi stener(Event. COMPLETE, completeLi stener); urlLoader.1oad(new URLRequest(«someFi1e. txt»));

}

private function completeListener (e:Event):void { trace(e. currentTarget == e. target): // Отображает: true

}

}

}

Тем не менее, как будет рассказано в гл. 21, для событий, получателями которых являются отображаемые объекты в иерархии отображения, приемники могут регистрироваться как в получателе события, так и в его контейнерах отображения. Для приемников событий, зарегистрированных в контейнере отображения получателя события, переменная currentTarget ссылается на этот контейнер, а переменная target — на объект получателя события.

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

Предположим, объект Sprite, содержащий объект TextField, зарегистрировал приемник события clickListener ( ) для события MouseEvent. CLICK. Когда пользователь щелкает кнопкой мыши в этом текстовом поле, происходит диспетчеризация события MouseEvent. CLICK, в результате чего вызывается приемник clickListener ( ). Внутри метода clickListener ( ) переменная currentTarget ссылается на объект Sprite, а переменная target — на объект TextField.

Программы обычно используют переменную currentTarget для управления объектом, в котором зарегистрирован приемник. В качестве примера модифицируем функцию f ocusInListener ( ) из листинга 12.3. На этот раз при получении объектом TextField фокуса ввода наш новый код функции focusInListener ( ) будет отображать синий овал вокруг текстового поля. Синий овал рисуется на объекте Sprite, доступ к которому осуществляется через переменную currentTarget.

public function focusInListener (e:FocusEvent):void { // Установить для фона текстового поля красный цвет TextField(e. target).backgroundColor = OxFFOOOO;

// Получить ссылку на объект Sprite

var theSprite:Sprite = Sprite(e. currentTarget);

// Нарисовать эллипс на объекте Sprite theSpri te. graphi cs. begi nFi11(OxOOOOFF): theSprite. graphics. drawEl1ipse(-10. -10. 75. 40):

Отмена стандартного поведения событий

Некоторые события в языке ActionScript обладают побочным эффектом, называемым стандартным поведением. Например, стандартным поведением события TextEvent. TEXT_INPUT является добавление текста в текстовое поле получателя. Подобным образом стандартным поведением события MouseEvent. MOUSE_DOWN, получаемого объектом класса SimpleButton, является отображение картинки, которая представляет нажатое состояние кнопки.

В некоторых случаях события, которые характеризуются стандартным поведением, предоставляют возможность избежать этого поведения программным путем. Такие события называются отменяемыми. Например, отменяемыми являются события TextEvent. TEXT_INPUT, FocusEvent. KEY_FOCUS_CHANGE и FocusEvent. MOUSE_FOCUS_CHANGE.

Чтобы избежать стандартного поведения для отменяемого события, мы вызываем метод 3K3eMR7mpapreventDef ault ( ) класса Event над объектом этого класса, передаваемым во все приемники, зарегистрированные для данного события. Например, в следующем коде мы отменяем стандартное поведение для всех событий TextEvent. TEXT_INPUT, получателем которых является текстовое поле t. Вместо того чтобы позволить программе отображать в текстовом поле текст, вводимый пользователем, мы будем просто добавлять в это поле букву «х».

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

// Изменить текст, вводимый пользователем, на символ «х» public class InputConverter extends Sprite { private var t:TextField;

public function InputConverter ( ) { // Создаем текстовое поле t = new TextField( ); t. border = true; t. background = true; t. type = TextFieldType. INPUT addChild(t);

// Регистрируем приемник для события TextEvent. TEXT_INPUT t. addEventListener(TextEvent. TEXT_INPUT, textInputListener);

}

// Приемник выполняется при возникновении события TextEvent. TEXT_INPUT private function textInputListener (e:TextEvent):void {

// Показать текст, введенный пользователем

trace(«Attempted text input: » + e. text);

// Исключить отображение введенного текста в текстовом поле e. preventDefault( );

// Добавить в текстовое поле букву «х» вместо введенного // пользователем текста t. appendTextCx»);

Чтобы определить, обладает ли определенное событие стандартным поведением, которое можно отменить, проверьте значение переменной экземпляра cancelable класса Event внутри приемника, зарегистрированного для получения уведомлений о возникновении данного события. Для предопределенных событий эту информацию можно также найти в разделе описания соответствующего события в справочнике по языку ActionScript корпорации Adobe.

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

Чтобы определить, было ли отменено стандартное поведение события, диспетчеризация которого происходит в текущий момент, проверьте возвращаемое значение метода экземпляра isDefault Prevented ( ) класса Event внутри приемника, зарегистрированного для получения уведомлений о возникновении данного события.

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

Еще один пример, демонстрирующий использование метода preventDefault() для события TextEvent. TEXT_INPUT, показан в листинге 22.8 гл. 22.

Приоритет приемника события

По умолчанию, если сразу несколько приемников событий регистрируются в конкретном объекте для получения уведомлений об одном и том же типе событий, они вызываются в том порядке, в котором были зарегистрированы. Например, в следующем коде два приемника событий — completeListenerA ( ) и completeListenerB ( ) — регистрируются в объекте urlLoader для получения уведомлений о событии Event. COMPLETE. При возникновении события Event-COMPLETE приемник completeListenerA ( ) будет выполнен раньше приемника completeListenerB ( ), поскольку completeListenerA ( ) был зарегистрирован раньше completeListenerB ( ).

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

public class FileLoader extends Sprite { public function FileLoader ( ) { var url Loader-.URLLoader = new URLLoader( ); // Порядок регистрации определяет порядок выполнения urlLoader. addEventLi stener(Event. COMPLETE, completeLi stenerA);

url Loader. addEventLi stener(Event. COMPLETE. completeListenerB); url Loader.1oad(new URLRequest(«someFi1e. txt»)):

}

private function completeListenerA (e:Event):void { traceCListener A: Load complete»);

}

private function completeListenerB (e:Event):void { traceCListener B: Load complete»);

}

}

}

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

addEventLi stener(тия, приемник, использоватьПерехват, приоритет, использоватьСлабуюСсылку)

Параметр приоритет представляет собой целое число, обозначающее порядок, в котором должен вызываться регистрируемый приемник события относительно других приемников, зарегистрированных для того же события в том же объекте. Приемники, зарегистрированные с более высоким значением параметра приоритет, будут вызваны раньше приемников, зарегистрированных с более низким значением. Например, приемник, зарегистрированный со значением 3 параметра приоритет, будет вызван раньше приемника, зарегистрированного со значением 2 параметра приоритет. Если два приемника зарегистрированы с одним и тем же значением параметра приоритет, они будут выполняться в том порядке, в котором были зарегистрированы. Если значение параметра приоритет не указано, принимается значение по умолчанию, равное 0.

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

Следующий код демонстрирует принципы использования параметра приоритет. Приемник completeListenerB ( ) будет выполнен раньше приемника completeListenerA ( ) несмотря на то, что completeListenerA ( ) был зарегистрирован раньше completeListenerB ( ).

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

public class FileLoader extends Sprite { public function FileLoader ( ) { var urlLoader:URLLoader = new URLLoader( ); // Параметр приоритет определяет порядок выполнения urlLoader. addEventLi stener(Event. COMPLETE.

completeListenerA,

false.

0);

url Loader. addEventLi stener(Event. COMPLETE.

completeListenerB.

false, 1);

urlLoader.1oad(new URLRequest(«someFi1e. txt»));

}

private function completeListenerA (e:Event):void traceCListener A: Load complete»);

private function completeListenerB (e;Event);void { traceCListener B: Load complete»);

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

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

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

Приемники событий и управление памятью

В этой главе мы увидели, что событийная модель языка ActionScript основывается на приемнике (представляющем собой функцию или метод) и объекте, в котором регистрируется этот приемник. Каждый объект, регистрирующий приемник для определенного события, поддерживает связь с этим приемником, сохраняя ссылку на него во внутреннем массиве, который называется списком приемников. Например, в следующем коде (взятом из листинга 12.1) метод completeListener ( ) регистрируется в объекте urlLoader для событий Event. COMPLETE. В результате внутренний список приемников объекта ur lLoader получает ссылку на метод completeListener ( ).

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

public class FileLoader extends Sprite { public function FileLoader ( ) { var urlLoader:URLLoader = new URLLoaderC );

// Регистрация приемника completeListener( )

url Loader. addEventLi stener(Event. COMPLETE, completeLi stener);

urlLoader.1oad(new URLRequestC»someFi1e. txt»));

private function completeListener (e:Event):void { traceC’Load complete»);

}

}

}

По умолчанию любой объект, получивший ссылку на приемник, хранит ее до тех пор, пока регистрация данного приемника не будет явно отменена методом removeEventListener ( ). Более того, объект продолжает хранить полученную ссылку на приемник даже тогда, когда в программе не остается других ссылок на этот приемник. Это демонстрирует следующий простой класс AnonymousLi stener. Он создает анонимную функцию и регистрирует ее для событий MouseEvent. MOUSE_EVENT в экземпляре Stage клиентской среды выполнения Flash. Хотя класс AnonymousLi stener не имеет ссылок на эту анонимную функцию, она продолжает храниться в экземпляре Stage и вызывается каждый раз при возникновении события MouseEvent. MOUSE MOVE, даже спустя долгое время после завершения метода конструктора класса AnonymousListener.

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

Package { import flash. display.*; import flash. events.*;

public class AnonymousListener extends Sprite { public function AnonymousListener ( ) { // Добавляем анонимную функцию в список приемников экземпляра Stage stage. addEventLi stener(MouseEvent. M0USE_M0VE.

function (e:MouseEvent):void {

trace(«mouse move»); }):

}

}

}

В предыдущем коде созданная анонимная функция окажется навсегда «заброшенной» в списке приемников экземпляра Stage. Программа не сможет отменить регистрацию анонимной функции, поскольку она не имеет ссылки на нее.

«Заброшенные» приемники представляют собой потенциальный источник существенного расходования памяти и могут привести к другим побочным эффектам в программах на языке ActionScript.

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

Предположим, мы разрабатываем игру, которая заключается в ловле бабочек: чтобы поймать бабочку, игрок должен коснуться ее указателем мыши. Бабочки

не хотят быть пойманными и всячески стараются улететь от указателя мыши. Основным классом приложения является ButterflyGame. Каждая бабочка представляется экземпляром класса Butterfly. Для управления перемещением бабочек в игре используется основной объект Timer, который генерирует событие TimerEvent. TIMER каждые 25 мс. Каждый объект класса Butterfly регистрирует приемник в основном объекте Timer и вычисляет свое новое местоположение всякий раз, когда возникает событие TimerEvent. TIMER.

Рассмотрим код класса Butterfly:

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

public class Butterfly extends Sprite { // Каждый объект Butterfly получает ссылку на основной таймер // через параметр конструктора gameTimer public function Butterfly (gameTimer:Timer) { gameTimer. addEventListener(TimerEvent. TIMER. timerListener);

}

private function timerListener (e:TimerEvent):void { traceC’Calculating new butterfly position…»); // Вычисление нового местоположения бабочки (код не приводится)

}

}

}

Рассмотрим код класса ButterflyGame, который значительно упрощен, чтобы акцентировать ваше внимание на коде, отвечающем за создание и удаление бабочки. В этой версии кода игра содержит только одну бабочку.

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

Package { import flash. display.*; import flash. utils.*;

public class ButterflyGame extends Sprite { private var timer:Timer; private var butterfly:Butterfly;

public function ButterflyGame ( ) { // Игровой таймер timer = new Timer(25. 0); timer. start( ); addButterfly( );

}

// Добавляет бабочку в игру public function addButterfly ( ):void { butterfly = new Butterfly(timer);

// Удаляет бабочку из игры public function removeButterfly ( ):void { butterfly = null:

}

}

}

Для добавления бабочки в игру класс ButterflyGame использует следующий код: butterfly = new Butterfly(timer);

Этот код приведет к выполнению конструктора класса Butterfly, в результате чего метод timerListener ( ) класса Butterfly зарегистрируется в объекте gameTimer для получения событий TimerEvent. TIMER.

Когда игрок поймает бабочку, объект ButterflyGame удалит соответствующий объект Butterfly из программы, используя следующий код:

butterfly = null;

Однако, даже несмотря на то, что предыдущий код удаляет ссылку на объект Butte rfly из объекта ButterflyGame, в списке приемников объекта gameTimer продолжает храниться ссылка на метод timerListener ( ) объекта Butterfly и, соответственно, на сам объект Butterfly. Более того, метод timerListener ( ) продолжает выполняться каждый раз при возникновении события TimerEvent. TIMER. Таким образом, объект Butterfly продолжает потреблять память и процессорное время и способен вызвать неожиданные или нежелательные побочные эффекты в программе. Чтобы избежать подобных проблем, перед удалением объекта Butterfly из игры нужно сначала отменить регистрацию метода timerListener ( ) для событий TimerEvent. TIMER.

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

Добавим новую переменную gameTimer и новый метод destroy ( ) в класс Butterfly, чтобы облегчить процесс отмены регистрации приемника для события TimerEvent. TIMER. Основной игровой таймер присваивается переменной gameTimer. Метод destroy ( ) отменяет регистрацию метода timerListener ( ) для событий TimerEvent. TIMER. Рассмотрим код класса Butterfly с внесенными изменениями, которые выделены полужирным шрифтом:

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

public class Butterfly extends Sprite { private var gameTimer:Timer;

public function Butterfly (gameTimer:Timer) { this. gameTimer = gameTimer;

this. gameTi mer. addEventLi stener(Ti merEvent. TIMER. timerLi stener);

}

private function timerListener (e:TimerEvent):void { traceCCalculating new butterfly position…»);

// Вычисление нового местоположения бабочки (код не показан)

public function destroy ( ):void { gameTimer. removeEventListener(TimerEvent. TIMER, timerListener);

}

}

}

Перед тем как удалить ссылку на объект Butterfly, в методе экземпляра removeButterfly ( ) класса ButterflyGame мы вызываем метод destroy ( ), как показано в следующем коде:

public function removeButterfly ( ):void { butterfly. destroy( );

butterfly = null;

}

Вызывая метод destroy ( ) перед удалением объекта Butterfly из игры, мы не позволяем методу timerListener ( ) оказаться «заброшенным» в списке приемников объекта Timer.

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

1 Если вы регистрируете приемник события в каком-либо объекте, убедитесь, что ваша

мй’ J. программа с течением времени также отменяет регистрацию этого приемника.

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

^ I Для изучения этого раздела требуется предварительное понимание механизма сборки??, 4 « мусора в языке ActionScript. Этот механизм рассматривается в гл. 14.

W:_

Регистрация приемника с использованием параметра использоватьСлабуюСсылку, для которого установлено значение true, не позволит этому приемнику оказаться «заброшенным» в списке приемников объекта, выполняющего регистрацию. Предположим, что объект (О) регистрирует приемник (П) для события (С) с использованием параметра использоватьСлабуюСсылку, для которого установлено значение true. Кроме того, предположим, что единственной ссылкой на П, которой обладает программа, является ссылка, хранящаяся в О. В обычной ситуации п будет храниться в О до тех пор, пока регистрация П для события С не будет отменена. Однако поскольку при регистрации П был использован параметр использоватьСлабуюСсылку со значением true и О хранит единственную оставшуюся ссылку на П в программе, П сразу же становится пригодным для сборки мусора. Впоследствии сборщик мусора по своему усмотрению может автоматически исключить П из списка приемников О и удалить его из памяти.

Для демонстрации работы параметра использоватьСлабуюСсылку вернемся к классу AnonymousListener. Как уже говорилось, класс AnonymousListener создает

анонимную функцию и регистрирует ее для событий MouseEvent. MOUSE_MOVE в экземпляре Stage клиентской среды выполнения Flash. Однако на этот раз при регистрации функции для событий MouseEvent. MOUSE_MOVE мы используем параметр использоватьСлабуюСсылку со значением true.

package { import Л ash. display.*; import flash. events.*;

public class AnonymousListener extends Sprite { public function AnonymousListener ( ) { // Добавляем анонимную функцию в список приемников экземпляра Stage stage. addEventListener(MouseEvent. M0USE_M0VE.

function (e:MouseEvent):void { trace(«mouse move»);

}.

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

False. 0.

true);

}

После выполнения предыдущего кода единственной ссылкой на анонимную функцию в программе является ссылка, хранящаяся в экземпляре Stage. Поскольку анонимная функция была зарегистрирована с использованием параметра использоватьСлабуюСсылку, для которого было установлено значение true, она сразу же становится пригодной для сборки мусора. Таким образом, сборщик мусора по своему усмотрению может впоследствии автоматически исключить анонимную функцию из списка приемников экземпляра Stage и удалить ее из памяти.

Безусловно, тот факт, что анонимная функция пригодна для сборки мусора, совершенно не означает, что она будет удалена из памяти. На самом деле в случае с предыдущим простым примером, функция, скорее всего, не будет удалена из памяти, поскольку объем используемой приложением памяти недостаточен для запуска механизма сборки мусора. В результате функция будет продолжать выполняться при получении события MouseEvent. MOUSE_MOVE экземпляром Stage, хотя теоретически она может быть удалена из памяти в любой момент времени. Из этого следует, что вообще не следует полагаться на параметр исполь — зоватьСлабуюСсылку как на способ автоматического удаления приемников событий. Наилучшее решение — просто избегать появления заброшенных приемников событий.

*^ | Если вы регистрируете приемник события в каком-либо объекте, убедитесь, что ваша м& d m программа с течением времени также отменяет регистрацию этого приемника.

До сих пор в этой главе мы работали исключительно с предопределенными событиями языка ActionScript. Теперь рассмотрим процесс создания в программе своих собственных пользовательских событий.

Пользовательские события

Чтобы выполнить диспетчеризацию нового пользовательского события в языке ActionScript, достаточно расширить класс EventDispatcher, присвоить новому событию имя и вызвать метод экземпляра dispatchEvent ( ) класса EventDispatcher. Для изучения процесса создания пользовательских событий в программе мы рассмотрим два примера: в первом событие создается для игры, а во втором — для элемента пользовательского интерфейса.

‘ * . I Если вы хотите сделать получателем события экземпляр класса, который уже рас-м$4г« ширяет другой класс, используйте композиционный подход, рассмотренный в гл. 9: Яу непосредственно реализуйте интерфейс IEventDispatcher и используйте методы класса EventDispatcher не через наследование, а через композицию.

Пользовательское событие «gameOver»

Предположим, что мы создаем универсальный каркас для разработки видеоигр. Каркас включает в себя следующие два класса: класс Game, выполняющий основные функции, необходимые для любой видеоигры, и класс Console, который представляет панель для запуска новых игр. Каждый раз при запуске новой игры класс Console создает объект класса Game. Любой экземпляр класса Game, создаваемый классом Console, является получателем пользовательского события «gameOver», которое возникает после окончания игры.

Чтобы объекты класса Game могли выступать в роли получателей событий, класс Game расширяет класс EventDispatcher, как показано в следующем коде:

package { import flash. events.*;

public class Game extends EventDispatcher { }

}

Кроме того, в классе Game определена константа Game. GAME_OVER, значением которой является имя пользовательского события: «gameOver». По соглашению имена констант событий записываются полностью прописными буквами, а слова разделяются знаком подчеркивания, например: GAME_OVER. Константы пользовательских событий обычно определяются либо в классе получателя события (в данном случае в классе Game), либо, если используется подкласс класса Event, в этом подклассе (как показано в нашем следующем примере с элементом пользовательского интерфейса). Поскольку в нашем текущем примере подклассы класса Event не используются, определим константу для события «gameOver» в классе Game, как показано в следующем коде:

package { import flash. events.*;

public class Game extends EventDispatcher { public static const GAME OVER:String = «gameOver»;

}

}

Когда игра завершается, объект Game вызывает метод endGame ( ), который возвращает игровую среду в исходное состояние, что позволит начать новую игру. Вот код метода endGame ( ):

package { import flash. events.*;

public class Game extends EventDispatcher { public static const GAME_0VER:String = «gameOver»;

private function endGame ( ):void { // Выполнение действий для завершения игры (код не показан)

}

}

}

Когда все действия, завершающие игру, выполнены, метод endGame ( ) использует метод dispatchEvent ( ), чтобы приступить к диспетчеризации события Game. GAME_OVER, сигнализирующего об окончании игры:

package { import flash. events.*;

public class Game extends EventDispatcher { public static const GAME_0VER:String = «gameOver»;

private function endGame ( ):void { // Выполнение действий для завершения игры // (код не показан)

// …после чего просим среду Flash выполнить

// диспетчеризацию события, обозначающего окончание игры

dispatchEvent(new Event(Game. GAME_OVER));

}

}

}

Обратите внимание, что, поскольку метод dispatchEvent ( ) вызывается над

объектом Game, этот объект и является получателем события.



Полезные ссылки
Случайные записи
  • 27.08.2011">Тесты ноутбуков с Blu-Ray Acer Aspire 6935G и 8930G
  • 14.03.2011">Руководство по actionscript. часть 3, стр. 066
  • 12.03.2011">Руководство по actionscript. часть 3, стр. 124
  • 24.02.2011">Руководство по actionscript. часть 7, стр. 003
  • 18.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.58
  • 26.02.2011">Руководство по actionscript. часть 6, стр. 068
  • 18.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.65
  • 03.02.2010">Создание графиков и диаграмм на JavaScript
  • 22.03.2011">Руководство по actionscript. часть 2, стр. 018
  • 06.04.2010">Бесплатные шрифты
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.2
  • 03.01.2013">Жанр Tower Defence жив
  • 04.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.33
  • 05.03.2011">Руководство по actionscript. часть 5, стр. 024
  • 04.08.2011">Шторы на заказ – укрась свой родной дом
Опрос

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

View Results

Loading ... Loading ...