Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection /

Exploring Rich Island development with Flex Builder – Part 5: Animating with ActionScript

by Trilemetry

Trilemetry
  • Trilemetry, Inc.

Content

  • Reviewing the architecture
  • Creating and using a custom component
  • Using the DateFormatter class
  • Animating with the Timer class

Modified

3 August 2009

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
Flex 3 Flex Builder 3

Requirements

Prerequisite knowledge

  • Part 1: Getting started
  • Part 2: Laying out and styling visual elements
  • Part 3: Handling data and binding it to controls
  • Part 4: Creating a custom component

User level

All

Required products

  • Flash Builder (Download trial)

Sample files

  • sap_ri_pt5_starter.zip (25 KB)
  • sap_ri_pt5_solution.zip (26 KB)

Welcome to Part 5 of this tutorial series. In this part, you will create, style, and implement a custom clock component for the wagon assembly line application that is pictured in Figure 1. You will also implement the ActionScript Timer class to animate the clock and ensure that updates to the inventory quantities in the DataGrid control are properly reflected in the colored bins.

Note: Parts 1 through 6 focus on teaching Flex development to SAP developers. Part 7 teaches Flex developers how to create a Web Dynpro application. Parts 8 and 9 show both SAP and Flex developers how to integrate the Flex application as a Rich Island into the Web Dynpro application.

Working application showing bin color change and clock time
Figure 1. Working application showing bin color change and clock time

Reviewing the architecture

Figure 2 outlines the Flex framework controls and containers that you will use in this application. In this tutorial, you convert the clock display that you created in an earlier tutorial into a custom component by moving the Canvas container layout (the smaller image at the bottom right of Figure 2) from the main application file to a separate file.

The current application architecture
Figure 2. The current application architecture

Variables used

You used the following variables in previous tutorials:

  • _wagonInventory: The inventory data that will populate the colored bins.
  • _wagonProductionRate: The speed at which a complete wagon is assembled.
  • _wagonYellowWarningAmount: The minimum number of wagons that can be created, based on the available inventory, when a bin turns yellow.
  • _inventoryNeededForYellow: The number of parts needed in a bin to make the wagons for the _wagonYellowWarningAmount.
  • _wagonRedWarningAmount: The minimum number of wagons that can be created, based on available inventory, when you want the bin to turn red and the assembly line to stop.
  • _inventoryNeededForRed: The number of parts needed in the bin to make the wagons for the _wagonRedWarningAmount.
  • _binPosition: The index of the data in the _wagonInventory ArrayCollection that populates the bin.

This tutorial introduces the following variables:

  • _productionTimer: An instance of the Timer class that will generate timed events and animate the clock and bin displays based on the _wagonProductionRate.
  • _secondsToCreateAvailableWagons: The number of seconds needed to create wagons based on the bin with the lowest amount of inventory. This value will change appropriately as inventory is used.
  • _clockMinutes: The minutes calculated from the _secondsToCreateAvailableWagons for display in the clock.
  • _clockSeconds: The remaining seconds (after the minutes are calculated) for display in the clock.
  • _clockDisplay: An instance of the Date class that is used to create a time to display on the clock.
  • currentTime: The string representation of the Date object that will display on the clock.

Creating and using a custom component

In this section, you will replace the clock code from the main application MXML file with an equivalent custom component.

  1. In the Flex Builder Navigator view, right-click the components folder within the src folder of this project and select New > MXML Component.
  2. Name the component ClockComponent.

    The component is based on the Canvas container.

  3. Click Finish.
  4. In the ClockComponent.mxml file, locate the opening Canvas tag and delete the width and height properties.
  5. After the opening Canvas tag, create a Script block.
  6. Open ShopFloorBins.mxml from the src folder of the ShopFloorBins project.
  7. Locate the clockTitle Label control and the following timeLeftBackground Canvas container and its contents.
  8. Cut them from the code.
  9. Paste the code after the closing Script tag of the ClockComponent.mxml file.
  10. Save the file.
  11. Return to ShopFloorBins.mxml.
  12. Locate the clockBase Canvas container and delete it.
  13. Locate the assemblyLineBackground Canvas container and add the ClockComponent from the comp namespace before the first BinComponent component instance.

    Note: Remember that the comp namespace has been defined in the opening Application tag to point to all class files in the components directory of this Flex Builder project. Since ClockComponent.mxml is a class file within this directory, you can reference it from the namespace.

  14. Add the following properties and values to the ClockComponent instance:
<comp:ClockComponent id="clock" width="197" height="90" y="32" horizontalCenter="0"/>
  1. Save the file and run it.

    You should see the that clock is unformatted because the clockStyle styleName property has not been applied to ClockComponent.mxml.

The application uses the custom clock component.
Figure 3. The application uses the custom clock component.
  1. Return to ClockComponent.mxml.
  2. Between the Script block tags, declare a public variable named currentTime and data type it to the String class.

    The currentTime variable is the String used for the clock display. You are creating it as a public variable because it will be a property of the custom component that can be set in the ClockComponent instance of the main application.

  3. Make the currentTime variable Bindable.
[Bindable] public var currentTime:String;
  1. Locate the timeLeft Label control and change its id property value of 00 and bind the text property to currentTime.
<mx:Label id="timeLeft" text="{currentTime}" .../>

You now create a variable to hold the time and display it in the clock.

  1. Save the ClockComponent.mxml file.
  2. Return to ShopFloorBins.mxml.
  3. In the Script block below the other variable declarations, declare and instantiate a bindable private variable named _clockDisplay and data type it to the Date class.

    The _clockDisplay is an instance of the Date class that you will use to format and display the clock value. An instance of the Date class represents a particular point in time.

[Bindable] private var _clockDisplay:Date = new Date();
  1. Locate the clock component instance and add a currentTime property with a value bound to _clockDisplay.

    Note: Remember the currentTime is the public property of the ClockComponent that you created earlier. You are binding the _clockDisplay Date object in the main application to it.

<comp:ClockComponent id="clock" . . . currentTime="{_clockDisplay}"/>
  1. Save the file and run it.

You should see the time displayed as shown in Figure 4. The time has not been formatted yet as minutes and seconds.

The custom clock component displays the unformatted time.
Figure 4. The custom clock component displays the unformatted time.

Using the DateFormatter class

In this section you will use the DateFormatter ActionScript class to properly format the clock display.

  1. Return to ShopFloorBins.mxml.
  2. After the Script block but before the Canvas container, add the DateFormatter component with its id property value set to clockFormatter and its formatString property value set to NN:SS.

    Note: NN:SS is a format mask where NN represents minutes in an hour and SS represents seconds in a minute. Check the Flex documentation for more information about the available masks.

<mx:DateFormatter id="clockFormatter" formatString="NN:SS" />
  1. Locate the clock component instance and use the format() function of the clockFormatter class to format the _clockDisplay property and bind it to the currentTime property of the custom component.

    The format() function returns a String representation of the information contained in the Date object.

<comp:ClockComponent id="clock" . . . currentTime="{clockFormatter.format(_clockDisplay)}"/>
  1. Save the file and run it.

    As you can see in Figure 5, the clock component is not styled correctly.

The application with the clock component displays minutes and seconds.
Figure 5. The application with the clock component displays minutes and seconds.
  1. Open ClockComponent.mxml and add the styleName property to the opening Canvas tag with a value of clockStyle.

    Note: Remember, the clockStyle is defined in the CSS file named ShopFloor.css in the assets folder. It was not applied to the ClockComponent when the code was transferred from the main application.

  2. Save the file.
  3. Return to ShopFloorBins.mxml.
  4. Save the file and run it.

You should see that the clock is now properly formatted (see Figure 6).

The application with the clock component styled
Figure 6. The application with the clock component styled

Animating with the Timer class

In this section you calculate the count down time on the clock based on the available inventory in the bins. You will also use the ActionScript Timer class to animate the clock.

  1. Return to ShopFloorBins.mxml.
  2. Locate the _clockDisplay variable in the Script block and remove the assigned value.

    Note: Remember that this variable is a Date object that will hold the time, in minutes and seconds, for display in the clock. You need control as to when the value of currentTime is set which will happen in the function you create next.

[Bindable] private var _clockDisplay:Date;

Calculating bin with the lowest possible wagons

In this section, you calculate the total number of wagons that can be assembled based on the available inventory in each bin and the number of parts necessary from each bin to create a wagon.

  1. Locate the clock component instance and delete the currentTime property from the instance.

    The currentTime property will be populated within ActionScript through the logic of the application.

  2. In the Script block, create a bindable private variable named _secondsToCreateAvailableWagons and data type it to the uint class.

     

    This variable will hold the total number of seconds to make wagons based on available inventory. The DateFormatter object will also use this value to format the clock display. This variable will decrement as the clock counts down.

  3. Create a bindable private variable named _wagonProductionRate, data type it to the uint class and give it a value of 6.

    The production rate represents how fast a wagon can be assembled. In this case, every 6 seconds a wagon is assembled.

[Bindable] private var _secondsToCreateAvailableWagons:uint; [Bindable] private var _wagonProductionRate:uint = 6;
  1. In the Script block, create a private function named calculateMaxWagonsPossibleFromInventory() that takes no arguments and returns a void data type.
  2. Within the function create a local variable named potentialWagonsFromCurrentBin and data type it to the uint class.

    Note: This variable will hold the total number of wagons that can be created from the current bin as it loops over each bin in the ArrayCollection data. Note that local variables are not declared with an access modifier such as private or public. Their scope is automatically restricted to the function in which they are created.

private function calculateMaxWagonsPossibleFromInventory():void { var potentialWagonsFromCurrentBin:uint; }
  1. Create another local variable named lowestPotentialNumberOfWagons, data type it to the uint class.

    This variable will hold the value from the bin with the lowest number of possible wagons that can be created based on inventory.

private function calculateMaxWagonsPossibleFromInventory():void { var potentialWagonsFromCurrentBin:uint; var lowestPotentialNumberOfWagons:uint; }
  1. After the local variables, create a for loop to loop over the _wagonInventory ArrayCollection. In the for statement, create an iterant named x and data type it to the uint class with a value of 0 (zero). While x is less than _wagonInventory.length, iterate the loop using the x++ syntax.
for(var x:uint = 0; x < _wagonInventory.length; x++) { }
  1. Within the loop, assign the result of the current index of the _wagonInventory Quantity divided by the current index of the _wagonInventory Parts to the potentialWagonsFromCurrentBin variable. Use the getItemAt() function.

    As you perform the loop, you will divide the available inventory by the number of parts necessary for each wagon. In other words, if you have 100 wheels (quantity) and you need 4 wheels per wagon (parts), then the potential number of wagons that you can create from the wheels bin is 25.

potentialWagonsFromCurrentBin = _wagonInventory.getItemAt(x).Quantity / _wagonInventory.getItemAt(x).Parts;
  1. After the variable, create a conditional statement that tests whether x is equivalent to 0 (zero) or potentialWagonsFromCurrentBin is less than lowestPotentialNumberOfWagons.

    This is either the first time through the loop or the number of wagons that can be created from the current evaluated bin is less than the number of wagons that can be created from any previously evaluated bin.

    Note: Remember that the lowestPotentialNumberOfWagons variable will hold the lowest number of wagons that can be created from all of the bins. In other words, as you loop over each bin, this conditional check is asking, "Does this bin that I am currently evaluating have a lower number of potential wagons than the previous bins that I have already evaluated?"

for(var x:uint = 0; x < _wagonInventory.length; x++) { potentialWagonsFromCurrentBin = _wagonInventory.getItemAt(x).Quantity / _wagonInventory.getItemAt(x).Parts; if (x == 0 || potentialWagonsFromCurrentBin < lowestPotentialNumberOfWagons) { } }
  1. If the conditional statement tests true, assign the potentialWagonsFromCurrentBin to lowestPotentialNumberOfWagons.

    This will set the number of wagons that can be created for this evaluated bin as the lowest possibility.

if (x == 0 || potentialWagonsFromCurrentBin < lowestPotentialNumberOfWagons) { lowestPotentialNumberOfWagons = potentialWagonsFromCurrentBin; }
  1. After the conditional statement but still inside the loop, create another conditional statement that tests whether the _wagonInventory quantity is less than the _wagonInventory parts. Use the getItemAt() function.

    This tests whether there are enough parts in the bin to create a full wagon.

  2. If the conditional statement tests true, assign 0 (zero) to lowestPotentialNumberOfWagons.

If there are not enough parts to make a wagon, then you will set the lowestPotentialNumberOfWagons to zero.

if (_wagonInventory.getItemAt(x).Quantity < _wagonInventory.getItemAt(x).Parts) { lowestPotentialNumberOfWagons = 0; }

Calculating the number of seconds to create possible wagons

In this section, you will use the result of the previous calculation to determine the number of seconds it will take to create the possible wagons.

  1. After the loop but still inside the calculateMaxWagonsPossibleFromInventory() function, assign the result of lowestPotentialNumberOfWagons multiplied by _wagonProductionRate to the _secondsToCreateAvailableWagons variable.

    This calculates how many total seconds it takes to create all the wagons from the bin with the lowest inventory.

_secondsToCreateAvailableWagons = lowestPotentialNumberOfWagons * _wagonProductionRate;
  1. After this variable, call the setClockDisplay() function. You will create this function next.

You will call the setClockDisplay() function to display the countdown time in the clock component.

_secondsToCreateAvailableWagons = lowestPotentialNumberOfWagons * _wagonProductionRate; setClockDisplay();

Creating the clock display based on the inventory

In this section, you use the _secondsToCreateAvailableWagons value to calculate the minutes and seconds on the clock.

  1. In the Script block of ShopFloorBins.mxml, create a bindable private variable named _clockMinutes and data type it to the uint class.

    This variable will hold the value of total number of minutes it takes to make all the wagons.

  2. Create another bindable private variable named _clockSeconds and data type it to the uint class.

    This variable will hold the value of the remaining seconds after the minutes have been calculated.

[Bindable] private var _clockMinutes:uint; [Bindable] private var _clockSeconds:uint;
  1. Before the end of the Script block, create a private function named setClockDisplay() that takes no arguments and returns a void data type.
private function setClockDisplay(): void { }
  1. In the function, calculate the result of _secondsToCreateAvailableWagons divided by 60 using the Math.floor() function and assign the results to _clockMinutes.

    This calculates the number of minutes.

  2. Next, assign the result of _secondsToCreateAvailableWagons mod (using %) 60 to _clockSeconds.

    This calculates the remaining number of seconds using the modulus (%) operator. Remember that the modulus operator divides the _secondsToCreateAvailableWagons variable value by 60 and returns the remainder.

private function setClockDisplay(): void { _clockMinutes = Math.floor(_secondsToCreateAvailableWagons/60); _clockSeconds = _secondsToCreateAvailableWagons % 60; }
  1. Next, instantiate the _clockDisplay variable to the Date class.
_clockMinutes = Math.floor(_secondsToCreateAvailableWagons /60); _clockSeconds = _secondsToCreateAvailableWagons % 60; _clockDisplay = new Date();
  1. Use the setMinutes() method of the Date class to populate the new Date object by passing _clockMinutes and _clockSeconds as the two arguments.
_clockDisplay = new Date(); _clockDisplay.setMinutes(_clockMinutes,_clockSeconds);
  1. Using the clockFormatter.format() function to format _clockDisplay and assign it to the text property of clock.

    This uses the previously defined DateFormatter instance to format the Date object display.

private function setClockDisplay(): void { _clockMinutes = Math.floor(_secondsToCreateAvailableWagons /60); _clockSeconds = _secondsToCreateAvailableWagons % 60; _clockDisplay = new Date(); _clockDisplay.setMinutes(_clockMinutes,_clockSeconds); clock.currentTime = clockFormatter.format(_clockDisplay); }
  1. Locate the startProduction() function in the Script block.
  2. Before the ending curly brace, add the function call to calculateMaxWagonsPossibleFromInventory().

    This ensures that the possible number of wagons are calculated and that the clock is formatted with the correct number of minutes and seconds when the application is initialized.

populateData(); calculateMaxWagonsPossibleFromInventory();
  1. Save the file and run it.

You should see the clock display a starting time based on the Handles bin because it has the lowest inventory. Change any of the bins to 50. Note that the clock time does not change.

The calculated time until shutdown is displayed in the clock.
Figure 7. The calculated time until shutdown is displayed in the clock.

Animating the clock

In this section, you animate the clock using the ActionScript Timer class.

  1. Open ShopFloorBins.mxml.
  2. In the Script block, import the Timer class from the flash.utils package.
  3. Also import the TimerEvent class from the flash.events package.
import flash.utils.Timer; import flash.events.TimerEvent;
  1. After the other variable declarations in the Script block, create a private variable named _productionTimer and data type it to the Timer class.
private var _productionTimer:Timer;

This variable is an instance of the Timer class that you will use to animate the clock.

  1. Locate the startProduction() function in the Script block.
  2. After the current code in the function, instantiate _productionTimer to the Timer class and pass 1000 and 0 as arguments.

    This code instantiates a Timer instance that will dispatch every second (1000 milliseconds) and will not stop. The second argument, set to zero here, instructs the timer to not stop. If you set this value to a number, the timer will stop in that number of milliseconds.

private function startProduction():void { populateData(); calculateMaxWagonsPossibleFromInventory(); _productionTimer = new Timer(1000, 0); }
  1. Next, add an event listener to the _productionTimer instance that takes TimerEvent.TIMER and timerEventHandler as arguments.

    Note: Event listeners execute code in response to specific events. The first argument declares the type of event to be handled and the second argument declares the name of the function that will execute. You will create the timerEventHandler() function next.

_productionTimer = new Timer(1000, 0); _productionTimer.addEventListener(TimerEvent.TIMER, timerEventHandler);
  1. Before the end of the Script block, create a private function named timerEventHandler() that takes one argument named event. Data type the argument to the TimerEvent class and set the function to return a void data type.
private function timerEventHandler(event:TimerEvent):void { }
  1. Within the function, decrement the _secondsToCreateAvailableWagons variable using -- syntax.

    When the timerEventHandler() function is called every second (1000 milliseconds), you will decrement the display on the clock by one second.

  2. Next, call the setClockDisplay() function.
private function timerEventHandler(event:TimerEvent):void { _secondsToCreateAvailableWagons--; setClockDisplay(); }

Now that there is one less second on the clock, you call the function that recalculates the minutes and seconds and changes the clock display, which animates the clock.

Starting and stopping the clock

Earlier you created the timer instance to never stop. This means that you must explicitly call the stop() and start() methods of the Timer class to control the instance. In this section, you create the function to start the timer when the application is initialized and the function to stop the timer when the clock counts down to zero.

  1. Before the end of the Script block, create a private function named startTimer() that takes no arguments and returns a void data type.
  2. Within the function, start the timer using the start() function of _productionTimer instance.
private function startTimer():void { _productionTimer.start(); }
  1. Locate the startProduction() function and call the startTimer() function before the closing curly brace.

    This will start the timer when the application initializes.

_productionTimer.addEventListener(TimerEvent.TIMER, timerEventHandler); startTimer();
  1. Save the file and run it.

    You should see the clock counting down. If you wait until the clock counts down to zero, you see that the TimerEvent event is still being dispatched so the clock never stops.

  2. Below the startTimer() function, create a private function named stopTimer() that take no arguments and returns a void data type.
  3. Within the function stop the timer using the stop() function of _productionTimer.
private function stopTimer():void { _productionTimer.stop(); }
  1. Locate the timerEventHandler() function in the Script block and delete the code in the function.
  2. Within the function, create a conditional statement that tests if the value of _secondsToCreateAvailableWagons is equivalent to 0 (zero).
  3. It the test is true, call the stopTimer() function.
  4. Create an else statement to decrement the _secondsToCreateAvailableWagons variable if the conditional statement is false.
  5. Still within the else statement, call the setClockDisplay() function.
if(_secondsToCreateAvailableWagons == 0) { stopTimer(); } else { _secondsToCreateAvailableWagons--; setClockDisplay(); }
  1. Save the file and run it.

Let the clock count down to zero. You should see that the clock stops.

Where to go from here

In this tutorial, you created the custom clock component, styled it, and used it in the main application. You also used the ActionScript Timer class to animate the clock and the colored bins and stopped the clock when it reached zero.

If you are interested in learning more about the topics in this tutorial, refer to the following resources:

  • Formatting data (6:09)
  • Controlling time intervals

In Part 6, you will synchronize the clock with the wagon assembly so that, as the clock counts down, inventory will be removed from the colored bins based on the production rate.

Exploring Rich Island development with Flex Builder

For your reference, here are all the parts in this series:

  • SAP developers: Build an interactive Flex application from scratch
    • Part 1: Getting started
    • Part 2: Laying out and styling visual elements
    • Part 3: Handling data and binding it to controls
    • Part 4: Creating a custom component
    • Part 5: Animating with ActionScript
    • Part 6: Synchronizing data in components
  • Flex developers: Create a Web Dynpro application
    • Part 7: Creating an SAP application
  • Flex and SAP developers: Create a working Rich Island
    • Part 8: Creating a Rich Island and handling data
    • Part 9: Communicating between a Rich Island and a Web Dynpro application

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement