Chapter 5. Timeline Control

IN THIS CHAPTER

Jump Right In

Playhead Movement

Frame Labels

Frame Rate

A Simple Site or Application Structure

What’s Next?

In this chapter, you’ll learn some basic approaches to controlling timelines—both that of the main Flash movie and the movie clips it contains. We’ll divide our focus into three main areas:

  • Jump Right In. Change the frame rate of your SWF at runtime.

  • Playhead Movement. This includes stopping and playing the file, and going to a specific frame.

  • Frame Labels. Including improved playhead movement techniques without relying on frame numbers.

  • Frame Rates. Changing the movie’s frame rate to increase or decrease animation speed during playback.

  • A Simple Site or Application Structure. We’ll wrap up the chapter by building a project that combines timeline animation with ActionScript navigation. The project can be used as an example template for a multistate application or Flash-based website.

We’ll also take a look at an undocumented feature that allows you to add frame scripts to movie clips at runtime and show you a demo of how to create a flexible structure for a Flash website or application.

Jump Right In

We’ll start off with one of the most-desired features in the evolution of ActionScript: the ability to adjust the frame rate of a file with code. Consider a simple example that switches a SWF’s frame rate between 1 and 24 frames per second, with every click of the mouse. This script can be found in the frame_rate_trace.fla source file.

1    stage.frameRate = 24;
2
3    this.addEventListener(Event.ENTER_FRAME, onEnter, false, 0, true);
4    function onEnter(evt:Event):void {
5        trace(stage.frameRate);
6    }
7
8    stage.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);
9    function onClick(evt:MouseEvent):void {
10        if (stage.frameRate == 24) {
11            stage.frameRate = 1;
12        } else {
13            stage.frameRate = 24;
14        }
15    }

Line 1 shows a new stage property called frameRate, which is assigned to 24 frames per second. Lines 3 through 6 contain an enter frame listener that traces the current frame rate. Lines 8 through 15 contain a mouse click listener that will toggle the frame rate between 1 and 24 frames per second.

Note

Real-world frame rates vary based on many factors including the processing power of your computer, how hard it’s working at any given moment, and what your SWF is trying to do. Depending on these circumstances, among others, you’re likely to achieve a maximum frame rate between 60 and 120 frames per second.

Although you’re unlikely to see this performance anytime soon, it’s theoretically possible to assign a frame rate up to 1000 fps. Any assignment above that number will fall back to 1000.

Once you get this example working, experiment by adding visual assets, as in frame_rate_timeline_tween.fla, and watch your animations change. A similar example later in the chapter will show the speed of an animation change in response to buttons that increase or decrease the frame rate.

Playhead Movement

One of the most basic ActionScript skills you need to embrace is the ability to navigate within your Flash movies. You will often use these skills to control the playback of movie clips nested within your main movies.

Note

To review the basics of movie clips, consult Chapter 3 and Chapter 4.

The code in this chapter is straightforward enough that you can create your own examples to test the functionality discussed, if you want to use your own assets. We’ll cover the structural necessities for each example to make it easier for you to follow along using your own assets. In each section, we’ll also cite the sample file we’re using so you can consult that file if preferred.

Let’s start by covering the basic concept of stopping and starting playback of the main timeline or movie clip, and then add an initial jump to another frame. If you’re creating your own file, be sure it has a linear animation in one layer of the main timeline, and four buttons in one or more other layers that span the length of the animation. In other words, your buttons must be visible throughout the animation. Alternatively, you can open the sample file navigation_01.fla.

Figure 5-1 shows navigation_01.fla, which contains four timeline tweens of black circles. For added visual impact, the circles use the Invert blend mode (seen in the Display section of the Properties panel) to create an interesting optical illusion of rotating cylinders. We’ll be starting and stopping playback at any point, as well as jumping to a specific frame to start and stop playback (frame 1, in this example). Initially, we’ll rely on frame numbers to specify where to start and stop.

navigation_01.fla demonstrates simple navigation
Figure 5-1. navigation_01.fla demonstrates simple navigation

Placing a stop() action in any frame script is a means of halting playback of that timeline without user interaction—perhaps at the end of an animation or to support a menu or similar need to display a single frame. Only the timeline in which the stop() action is used will stop, so if the main timeline is stopped, movie clips will continue to animate.

Let’s take a more interactive approach and look at invoking the stop() action via user input, such as clicking a button. Line 1 of the following script is an event listener added to a button named stopBtn. It uses a mouse click to call onStopClick(), the function defined in Lines 3 through 5.

1    stopBtn.addEventListener(MouseEvent.CLICK, onStopClick,
2                             false, 0, true);
3    function onStopClick(evt:MouseEvent):void {
4        stop();
5    }

Note

If you don’t know about event listeners or typed arguments, consult Chapter 3 for more information. Be sure to pay particular attention to the sidebar Garbage Collection: A Recommended Optional Parameter for Event Listeners on weak references.

All playback of the main timeline will cease when the user clicks the button. Adding the following lines to the script will allow you to restart playback. The new code is similar to the previous example, but invokes the play() method from the playBtn instead. Using this pair of buttons, you can start and stop playback at any time without relocating the playback head in the process.

6    playBtn.addEventListener(MouseEvent.CLICK, onPlayClick,
7                             false, 0, true);
8    function onPlayClick(evt:MouseEvent):void {
9        play();
10    }

Note

Organizing your code is a very personal thing, subject to preference. Many coders group listener objects (like buttons) together and place the corresponding functions soon after. This allows you to look at all the buttons at once without scrolling through all the functions. Others like to group the listener object and function together so the functionality of the object is immediately apparent. This exercise demonstrates both styles.

When defining the listeners, the order in which these items exist in the same script typically doesn’t matter. (After you’ve programmed for a while, you may find a need to create a listener at a specific moment at runtime, such as when you click a button. In these cases, where you place your code will take on additional importance.) Adopt a style and organization that suits your habits and makes your code easiest to read.

Using stop() and play() in this fashion is useful for controlling a linear animation, much in the same way a YouTube controller bar might control video playback. However, it’s less common when using interactive menus, for example, because you typically want to jump to a specific point in your timeline before stopping or playing.

For example, you might have generic sections that could apply to any project, such as home, about, and help. If you were restricted to the use of stop() and play(), you would be forced to play through one section to get to another.

Adding again to the previous script, the following content adds a slight variation. The buttons in the new script function in similar ways. However, instead of stopping in (or playing from) the current frame, the new buttons move the playhead to a specific frame first. For example, if you had previously stopped playback in frame 20, triggering play() again would send the playhead to frame 21. However, if you used gotoAndPlay() and specified frame 1 as a destination (as seen in line 16 of the script that follows), you would resume playback at frame 1, rather than at frame 21. If you use gotoAndStop() (as in line 19), the playhead will go to that frame but not continue to play through the rest of the timeline. There are no structural differences in this code, so simply add the following content to your existing script:

11    gotoPlayBtn.addEventListener(MouseEvent.CLICK, onGotoPlayClick,
12                                 false, 0, true);
13    gotoStopBtn.addEventListener(MouseEvent.CLICK, onGotoStopClick,
14                                 false, 0, true);
15    function onGotoPlayClick(evt:MouseEvent):void {
16        gotoAndPlay(1);
17    }
18    function onGotoStopClick(evt:MouseEvent):void {
19        gotoAndStop(1);
20    }

Once you get a navigation system working, it may sometimes be useful to know where you are in a timeline, or how many frames the timeline contains. For example, you can determine if you’re in the last frame of a timeline by checking to see if the current frame matches the total number of frames. Tracing this information to the Output panel can help you track your movements during development. Tracing totalFrames will display the number of frames in the timeline, and tracing currentFrame will show the frame number in which the playhead currently sits.

trace("This movie has", totalFrames, "frames.");
trace(currentFrame);

The companion sample file, navigation_02.fla, demonstrates the use of these properties, tracing totalFrames in frame 1, and currentFrame each time a button is clicked.

Frame Labels

Using frame numbers with goto methods has advantages, among them simplicity and use in numeric contexts (such as with a loop or other type of counter when an integer is at hand). However, frame numbers also have disadvantages. The most notable disadvantage is that edits made to your file after your script is written may result in a change to the number of frames, or frame sequence, in your timeline.

Note

A frame number is always an integer equal to, or greater than 1. A frame label is always a string.

For example, your help section may start at frame 100, but you may then insert or delete frames in a section of your timeline prior to that frame. This change may cause the help section to shift to a new frame. If your navigation script sends the playhead to frame 100, you will no longer see the help section.

One way around this problem is to use frame labels to mark the location of a specific segment of your timeline. As long as you shift content by inserting or deleting frames to all layers in your timeline (maintaining sync among your layers), a frame label will move with your content.

This is a useful feature when you are relying heavily on timeline tweens for file structure or transitions (as we’ll see in our demo site in a short while), or when you think you may be adding or deleting sections in your file. Frame labels remove the need to organize your content linearly and free you to rearrange your timeline at any point.

The sample file, frame_labels_01.fla, demonstrates the use of frame labels instead of frame numbers when using a goto method. It also illustrates another important and useful concept, which is that you can use these methods to control the playback of movie clips as well as the main timeline.

Instead of controlling the playback of a linear animation, the sample file moves the playhead between the frames of a movie clip called pages. This is a common technique for swapping content in a Flash file because you can keep your main timeline simple, and jump the movie clip from frame to frame to reveal each new screen. Figure 5-2 displays the “page1” frame of the pages movie clip in frame_labels_01.fla, after jumping to the frame by specifying the frame label. The timeline inset shows the frame labels.

The initial setup of this example requires that we prevent the movie clip from playing on its own, so we can exert the desired control over its playback. There are several ways to do this. The first, and perhaps most obvious approach, is to put a stop() action in the first frame of the movie clip.

The “page1” frame of the pages movie clip in frame_labels_01.fla
Figure 5-2. The “page1” frame of the pages movie clip in frame_labels_01.fla

The second technique is more flexible and easier to maintain because it centralizes your code into fewer frames. Use the stop() method, but in your main timeline, targeting the movie clip instance. To do this, precede the method with the object you wish to stop, as seen in line 1 of the following script. In this case, we are stopping the movie clip called pages. Immediately upon starting, the SWF stops the pages movie clip in line 1. Each button causes the movie clip to change frames in lines 8, 11, and 14.

1    pages.stop();
2
3    one.addEventListener(MouseEvent.CLICK, onOneClick, false, 0, true);
4    two.addEventListener(MouseEvent.CLICK, onTwoClick, false, 0, true);
5    three.addEventListener(MouseEvent.CLICK, onThreeClick,
6                           false, 0, true);
7    function onOneClick(evt:MouseEvent):void {
8        pages.gotoAndStop("page1");
9    }
10    function onTwoClick(evt:MouseEvent):void {
11        pages.gotoAndStop("page2");
12    }
13    function onThreeClick(evt:MouseEvent):void {
14        pages.gotoAndStop("page3");
15    }

To test the effectiveness of using frame labels, add or delete frames across all layers before one of the existing frame labels. Despite changing the frame count, you will find that the navigation still works as desired.

New Timeline ActionScript

ActionScript 3.0 provides a few new features relevant to timelines. The first is an associative array of all frame labels in a file. This array is called labels, and contains name and frame properties that provide the text of the frame label and the frame number to which it is applied.

The second is a scenes array that contains each scene’s name and number of frames, stored in the array’s name and numFrames properties, respectively. The scenes array also has its own labels object so you can check the label names and frame numbers as described previously, in all the scenes in your file.

Note

In case you’re unfamiliar with scenes, they’re essentially a way of organizing very long timelines into smaller manageable chunks. At runtime, all scenes are treated as one giant timeline, and the playhead can move freely between scenes either automatically during linear playback, or with ActionScript.

We don’t use scenes much in the work we do, but we’ve had students who rely on scenes to tell long stories through linear animation. Adding a new scene to a file (Window✓Other Panels✓Scene) effectively resets the interface to a new timeline, making it easier to work with the relevant frames without being distracted by prior or future scenes in your file. Another advantage of scenes is that you can test single scenes during development, rather than having to test your entire movie.

The sample file, frame_labels_02.fla, demonstrates several of these features, as well as illustrates a couple uses of the available frame label options. It uses the same pages movie clip as in the prior file, but with adapted functionality and buttons. Figure 5-3 shows the direct navigation to a frame that is four frames after a specified label.

The pages movie clip of frame_labels_02.fla jumping to a frame relative to the location of a label
Figure 5-3. The pages movie clip of frame_labels_02.fla jumping to a frame relative to the location of a label

We’re going to start by highlighting the functionality of the second button, output, which collects many of the features in one information dump to the Output panel. Looking at the following script, the first new item you’ll see is a main movie stop() action on line 1. This has been added because this file has a second scene to demonstrate the new scenes array and currentScene property. Line 3 stops the movie clip as in the prior example, and line 5 creates a mouse click listener for the button.

1    stop();
2
3    pages.stop();
4
5    output.addEventListener(MouseEvent.CLICK, onOutputClick,
6                            false, 0, true);
7    function onOutputClick(evt:MouseEvent):void {
8        trace("The main movie has " + scenes.length + " scenes.");
9        trace("The current scene is 0" + currentScene.name + "0.");
10        trace("It has " + currentScene.numFrames + " frame(s),");
11        trace(" and " + currentScene.labels.length + " label(s). ");
12        trace("The second scene's first label is 0" +
13            scenes[1].labels[0].name + "0,");
14        trace(" which is in frame " + scenes[1].labels[0].frame + ".");
15        var numLabels:int = pages.currentLabels.length;
16        trace("Movie clip 'pages' has " + numLabels + " labels.");
17        trace("Its last label is 0" +
18            pages.currentLabels[numLabels-1].name + "0.");
19    }

Note

In ActionScript 3.0, you can trace multiple items to the Output panel by separating them with commas when using the trace() statement. However, that will automatically put a space between each item in the trace. So, when you want to build a string with adjacent items, such as the single-quotation marks that surround some of the values in this script, it’s better to use the string concatenation operator (+) to join the items together, rather than use commas.

Lines 7 through 19 contain this button’s goodies, tracing the number of scenes (line 8), the name and number of frames of the current scene (lines 9 and 10), and the total number of labels in the current scene (line 11). The script also traces the name and frame number of the first label of the second scene (lines 12 through 14). Line 14 uses the array syntax discussed in Chapter 2, with indices starting at 0 to represent the first item in an array. Thus the code targets the second scene, first label, frame number.

Finally, lines 15 through 18 look at the currentLabels array of the pages movie clip, getting the number of labels through the length property, and the name of the last label in the clip.

This series of trace commands offers a half dozen or so variants on the new scene and label features and should stimulate your imagination. Try to figure out interesting ways to make use of these properties. To get you started, we’ve provided two examples, included on the other two buttons.

Attached to the first button, onePlus is a way of reaching a frame relative to a frame label. For instance, you may want to revisit a section of your file, but without retriggering an initialization routine found in the frame marked by your frame label. For example, a section may have an intro animation that you want to see the first time, but skip thereafter. In that case, you may want to go to the “label frame plus one.”

Perhaps more common is a uniformly structured file, such as a character animation cycle (walk, run, jump, duck, and so on), or an interface of drawers or tabs that slide in and out from off-stage. In these cases, each action might consist of the same number of frames. You may want to interrupt one sequence and jump to the same position in another sequence. Imagine, as an example, interrupting a timeline tween of an interface drawer sliding open, and wanting to jump to the same location in the timeline tween of the drawer sliding closed.

To avoid relying strictly on frame numbers, it helps to be able to start from a frame label and jump to a specific number of frames beyond that label. As an addition to your ongoing script, look at the following. This code sends the pages movie clip to a frame returned by the getFrame() function. In Line 21, the script passes in a label and a movie clip. The function, which we’ll look at in just a moment, returns the frame number that matches the label provided. In line 22, if the value returned is greater than zero (as all timelines start with frame 1), the movie clip is sent to that frame plus a relative offset of four additional frames.

20    onePlus.addEventListener(MouseEvent.CLICK, onOnePlusClick,
21                             false, 0, true);
22    function onOnePlusClick(evt:MouseEvent):void {
23        var frameNum:int = getFrame("page1", pages);
24        if (frameNum > 0) {
25            pages.gotoAndStop(frameNum + 4);
26        }
27    }
28
29    function getFrame(frLabel:String, mc:MovieClip):int {
30        for (var i:int = 0; i < mc.currentLabels.length; i++) {
31            if (mc.currentLabels[i].name == frLabel) {
32                return mc.currentLabels[i].frame;
33            }
34        }
35        return −1;
36    }

The aforementioned getFrame() function appears in lines 27 through 34. The function accepts a String parameter containing the name of the original frame label, and the movie clip within which the label resides. Note the int data type of the return value so the compiler knows to expect an integer from the function. Lines 28 and 29 loop through all the labels in the referenced movie clip, comparing the name of each label to the label desired. If a match is found, the frame in which the label resides is returned in line 30. If no match is found after looping through all the labels, −1 is returned in line 33. The desired result, in our sample file, is that the playhead jumps to frame 5 instead of frame 1 where the “page1” label resides.

Note

Although not universal, returning −1 when something isn’t found is a common technique. It may sound counterintuitive, but it came into popular use because zero is often a meaningful value. For example, both the first item in an array and the first character in a string have an index of zero.

In this example, you might choose to return 0 because you know there is no frame 0. However, maintaining consistency with other methods that return −1 when nothing is found will make things easier the more you code.

A similar coding technique is to use these features to check whether a specific frame exists. This option can be used for navigation error checking or simply to make sure you’re working with the correct movie clip among many that may be available.

The following code adds such a function and triggers it from a mouse click listener defined in lines 35 through 39. As before, the function call passes a label and movie clip to the function, as seen in line 38. The function itself is defined in lines 41 through 48, and is explained following the code.

37    labelCheck.addEventListener(MouseEvent.CLICK, onLabelCheckClick,
38                                false, 0, true);
39    function onLabelCheckClick(evt:MouseEvent):void {
40        trace(frameLabelExists("page3", pages));
41    }
42
43    function frameLabelExists(frLabel:String, mc:MovieClip):Boolean {
44        for (var i:int = 0; i < mc.currentLabels.length; i++) {
45            if (mc.currentLabels[i].name == frLabel) {
46                return true;
47            }
48        }
49        return false;
50    }

The functionality of isFrameLabel() is nearly the same as the getFrame() function discussed previously, except that this function returns true if a queried frame label is found, or false if it is not found. In our sample file, the third button will trace true to the Output panel, because the “page3” frame label does exist in the pages movie clip. This subtle variant is just another simple example of how you might use the frame label and scene arrays and properties introduced in ActionScript 3.0.

Frame Rate

As seen in the chapter’s opening script, you can now dynamically change the frame rate at which your file plays at runtime. In Flash Professional CS5, the default frame rate of an FLA is 24 frames per second, which can be adjusted in the Properties panel. Prior to ActionScript 3.0, the frame rate you chose was locked in for the life of your SWF. It is now possible to update the speed at which your file plays by changing the frameRate property of the stage, as demonstrated in the sample file frame_rate.fla.

Note

For more information about referencing the stage in ActionScript 3.0, see Chapter 3 and Chapter 4.

Figure 5-4 shows the interface of frame_rate.fla, which visualizes the runtime reassigning of frame rates.

frame_rate.fla with buttons on the left that increase and decrease the frame rate, which controls the speed of the animation on the right
Figure 5-4. frame_rate.fla with buttons on the left that increase and decrease the frame rate, which controls the speed of the animation on the right

The script in this file, shown in the following code block, increments or decrements the frame rate by five frames per second with each click of a button. You may also notice another simple example of error checking in the onSlowerClick() function, to prevent a frame rate of zero or below. Start the file and watch it run for a second or two at the default frame rate of 24 frames per second. Then experiment with additional frame rates to see how they change the movie clip animation.

1    info.text = stage.frameRate;
2
3    faster.addEventListener(MouseEvent.CLICK, onFasterClick,
4                            false, 0, true);
5    slower.addEventListener(MouseEvent.CLICK, onSlowerClick,
6                            false, 0, true);
7    function onFasterClick(evt:MouseEvent):void {
8        stage.frameRate += 5;
9        info.text = stage.frameRate;
10    }
11    function onSlowerClick(evt:MouseEvent):void {
12        if (stage.frameRate > 5) {
13            stage.frameRate -= 5;
14        }
15        info.text = stage.frameRate;
16    }

The frameRate property requires little explanation, but its impact should not be underestimated. Other interactive environments have long been able to vary playback speed, and this is a welcome change to ActionScript for many enthusiastic developers—especially animators. Slow motion has never been easier.

A Simple Site or Application Structure

As the final demo file in this chapter, we want to provide a very simple example of one of our most commonly requested uses of navigation to add visual interest. The demo_site.fla source file shows how to design a basic site or application skeleton that gives you the freedom to combine your timeline animation skills with ActionScript coding.

This file intentionally uses detailed, and varied, timeline tweens—with inconsistent frame counts—to transition between three separate sections of this sample site or application (Figure 5-5). The idea is to take advantage of frame label navigation, but freely move from any section to any other section without concern of interrupting (or matching) the entrance or exit animations.

As you look through the sample file, you’ll see that a virtual gamut of property manipulations add visual interest. Section 1 rotates in and skews out, section 2 bounces in and zooms out, and section 3 wipes in and fades out. Each section stops in the middle of the transitions to display its placeholder content. Moving unencumbered between any sections is achieved through a combination of the play() method and a variable.

The file demo_site.fla demonstrates navigation with transitions
Figure 5-5. The file demo_site.fla demonstrates navigation with transitions

The first script of this file is in frame 1 of the main timeline. Line 1 initializes the nextSection variable, typing it as a String. We will store the destination frame label in this variable. Scripts in other keyframes (which we’ll look at in a moment) will use the gotoAndPlay() method to jump to the frame stored in this variable.

1    var nextSection:String = "";
2
3    section1.addEventListener(MouseEvent.CLICK, navigate,
4                              false, 0, true);
5    section2.addEventListener(MouseEvent.CLICK, navigate,
6                              false, 0, true);
7    section3.addEventListener(MouseEvent.CLICK, navigate,
8                              false, 0, true);
9    function navigate(evt:MouseEvent):void {
10        nextSection = evt.target.name;
11        play();
12    }

The remainder of the script is similar to the previous examples, creating three buttons that all access the same listener. Line 10 populates the nextSection variable using the name of the button that was clicked. Knowing that the target property can identify the button that was clicked, we can further query its name property to determine the name of the button. By naming buttons with names that match frame labels, we can set up our file cleanly and efficiently. Clicking the section1 button will take us to the corresponding “section1” frame label.

Note

Chapter 3 discussed the use of the event argument in event listeners, and the ability to learn about the event trigger by querying its target property.

How, then, do we prevent the entry and exit animations from being interrupted or from overlapping? First, each button click populates the nextSection variable with the desired destination frame label. Then we use play() to play the file from that point forward. This plays through the entry animation of the first section, and then another script halts the playhead in the content keyframe of the section with a stop() action.

//at end of entry animation
stop();

Using the play() method prevents repeated clicks on a button from starting an entry animation over and over again—a possible side effect of using gotoAndPlay(). Instead of every click first jumping to a specific frame before playing, each click just continues to tell the timeline to play, which it’s already doing, and so has no ill effect.

Having stopped at the content frame of the section, the user is free to view that screen. Any subsequent button clicks will first populate the nextSection variable and then again call the play() method. This sets the playhead in motion, carrying it through the concluding animation until it hits the last frame script in the section:

//at end of exit animation
gotoAndPlay(nextSection);

This script is the last piece of the puzzle. After playing the prior section outro animation, this method sends the playhead to the new section entry animation. The cycle then repeats as the playhead dutifully stops at the content frame of the new section.

This structure allows you to be as creative as you want with timeline tweens and still move in and out of any section no matter how many frames each animation requires. Because you’re using frame labels, you can easily change any sequence without having to adjust your scripts to update new frame numbers.

What’s Next?

By now you should have a relatively firm grasp of how to navigate timelines, be able to manipulate display objects (including their properties and methods), and understand the fundamentals of the ActionScript 3.0 event model. Up to this point, we’ve been focusing primarily on syntax and approaching each task using simple procedural programming techniques.

As you’ll read in Chapter 6, you may find this sufficient for many of the projects you create. However, larger projects, and projects developed in a workgroup environment with multiple programmers, can significantly benefit from OOP techniques. From this point on, we’ll be using a little OOP in our demos, and you will eventually end up with a final project that is built entirely using object-oriented programming. This content design allows you to learn at your own pace, choosing when to use procedural programming and when to use OOP.

In the next chapter, we’ll introduce some basics of OOP, including:

  • Using encapsulation and polymorphism

  • Writing your first class

  • Creating a subclass that demonstrates inheritance

  • Organizing your classes and packages

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.194.44