Flash as3 show specific movie clip on click - actionscript-3

I just started using flash so this might be a simple thing so please be patient.
I'm doing a project were I have a map and you can click the places on the map and the info appears. I could manage to link every button_btn to every info_mc but that would take forever, so I tried to make a function that would read the buttons name and redirect to the info_mc itself. However since the name is being converted to string when I change from name_mc to name_info it is not redirecting. Here is the code.
winterfell_info.visible = false;
map.winterfell_btn.doubleclickEnabled = true;
map.winterfell_btn.mouseChildren = doubleClickEnabled;
map.winterfell_btn.addEventListener(MouseEvent.DOUBLE_CLICK, show_clicked);
function show_clicked(e:MouseEvent):void{
var get_name = e.currentTarget.name;
var open_info_string = get_name.slice(0,-3) + "info";
//I've tried some things here but nothing is working for me.
var open_info = stage.getChildByName(open_info_string);
open_info.visible = true;
}
Any help would be very much appreciated, thank you.
And if you wondering yes its a map for Game of Thrones.

Remove quotes around "open_info_string", as a first measure, and change the capital S in that string to normal one "s", because Actionscript is case sensitive.
var open_info = stage.getChildByName(open_info_string);
Then, if it wouldn't work at once, insert a trace(open_info_string); before you try to reach the movie clip, to find out whether it was cropped correctly.

i've done something similar.. i don't have time to adapt the code, but if you look at what I did here I believe it'll help you. I'm making an array with the instance names of objects on my video player and then created a function which adds the appropriate event listener using a switch case statement.. easiest way to do this in mass, in my opinion.
var buttons:Array = [clicky,pauseBtn,playBtn];
for (var i in buttons)
{
buttons[i].buttonMode = buttons[i].useHandCursor = true;
addEventListner(buttons[i]);
}
function addEventListner(movieClip)
{
movieClip.addEventListener(MouseEvent.CLICK, function(e){
e.stopPropagation();
switch (movieClip.name)
{
case "playBtn" :
youTubeHolder.videoPlayer.playVideo();
videoController.playBtn.visible = false;
videoController.pauseBtn.visible = true;
break;
case "pauseBtn" :
youTubeHolder.videoPlayer.pauseVideo();
videoController.playBtn.visible = true;
videoController.pauseBtn.visible = false;
break;
case "stopBtn" :
youTubeHolder.videoPlayer.seekTo(0);
youTubeHolder.videoPlayer.stopVideo();
break;
default :
trace(movieClip.name+"is not a valid button");
}
});
}

Related

how to load dynamic text sharedObject when flash is reopened in Actionscript 3?

I created a simple username page, in frame 1, there is a button and input text
like the code below
stop();
var SOlastFrame: SharedObject = SharedObject.getLocal("save_frame2");
var shared_data: String
next.addEventListener(MouseEvent.CLICK, gotomyNextFrame);
function gotomyNextFrame(e: MouseEvent): void {
shared_data = inputName.text
nextFrame()
SOlastFrame.data.lastframe = currentFrame;
SOlastFrame.flush();
}
if (SOlastFrame.data.lastframe != null) {
gotoAndStop(SOlastFrame.data.lastframe);
}
i saved the last frame by adding this code
SOlastFrame.data.lastframe = currentFrame;
SOlastFrame.flush();
so that I can jump to the last frame I opened, I added this code
if (SOlastFrame.data.lastframe != null) {
gotoAndStop(SOlastFrame.data.lastframe);
}
at frame 2, I put dynamic text
with code like this
var SOnameUser: SharedObject = SharedObject.getLocal("saveName");
SOnameUser.data.yourName = shared_data;
SOnameUser.flush();
trace(SOnameUser.data.yourName);
userName.text = "Hello " + shared_data;
if (shared_data != null) {
userName.text = shared_data;
SOnameUser.data.yourName = shared_data;
}
I think it works, dynamic text is saved successfully.
If I reopen it, it will go directly to frame 2 because of the execution result SOlastFrame.data.lastframe.
the problem is that the username that I saved earlier turns to null.
how can i load dynamic text on shareobject when SOlastFrame.data.lastframe is executed.
or
how to load sharedObject when flash is reopened
You get it like that because when you start 2-nd time you go straight to the frame 2 where the shared_data is empty. I think you should re-organize... well, everything.
// Frame 1: Splash (you don't have it).
// First of all, use a single SO rather than two.
var SO:SharedObject = SharedObject.getLocal("my.save");
// Check if there are saved fields.
if (SO.data.userName && SO.data.lastFrame)
{
// If there are saved credentials, the user
// won't even see the Login Frame.
gotoAndStop(SO.data.lastFrame);
}
else
{
// If there are no saved credentials — proceed to the Login Frame.
nextFrame();
}
Then, this is your frame 1.
// Frame 2: Login.
stop();
// You don't actually need to SO.flush() every time,
// it's a (feeble) measure against sudden crashes.
SO.data.lastFrame = currentFrame;
// Your code, for the most part, but simpler.
next.addEventListener(MouseEvent.CLICK, gotoNext);
function gotoNext(e:MouseEvent):void
{
// You don't need any additional variables
// to temporarily store the user name.
SO.data.userName = inputName.text;
nextFrame();
}
Then, here we go.
// Frame 3: only logged (via Login Frame or via SO data)
// users reach this point.
stop();
// In case we moved from the Login Frame.
SO.data.lastFrame = currentFrame;
// The only thing left to do.
userName.text = "Hello " + SO.data.userName;

Clear html5 canvas, then re-initialize it

I'm working on an activity where a user would draw a line chart and check to see how close it is to an actual chart. I've got it working, but need help on 'resetting' the canvas. I can get it to clear, but I need to reinitialize it after it's cleared so the user can try again.
here is the reset code I have so far:
var clearCanvas = false;
function onFrame(event) {
if (clearCanvas && project.activeLayer.hasChildren()) {
project.activeLayer.removeChildren();
clearCanvas = false;
}
}
$('.clear').on('click', function() {
clearCanvas = true;
return false
})
Here is a jsfiddle
I've kinda hacked this code together, as I'm not a coder, so go easy if it's not the best way of doing things. If anyone has a cleaner/more efficient way of doing this I'm all ears!
Thanks in advance.
S
First, your jsfiddle is getting a 404 when it tries to use "http://paperjs.org/static/js/paper.js". Switching that to "https://raw.github.com/paperjs/paper.js/master/dist/paper.js" works for me.
Second, if you want the user to try again, they need to be able to place 7 (or however many) new points after the reset, so in your onFrame function you need to set maxPoints to 0 when clearing the canvas.
Third, the path that connects the users points will need to be re-initialized. That can be done by calling path.removeSegments() and then re-adding path to the activeLayer.
Fourth, once you have removed the children from the activeLayer, fullChart (the "answer") is no longer on the activeLayer. You will have to re-add it and re-set its opacity to 0 (if you want it hidden again).
Putting those together, this onFrame seems to do what you are attempting:
function onFrame(event) {
if (clearCanvas && project.activeLayer.hasChildren()) {
project.activeLayer.removeChildren();
fullChart.opacity = 0;
maxPoints = 0;
path.removeSegments();
project.activeLayer.addChildren([fullChart, path]);
clearCanvas = false;
}
}
Here's a jsfiddle.
Other notes:
I don't believe you have to separate your code like that; you could just clear the canvas in your button's callback instead of waiting for the onFrame. So all of the code in onFrame could go in the callback instead (just without the clearCanvas variable).
I would also recommend using the same Point objects for your myCircle and myPath instead of making new ones with the same coordinates. For example:
var point1 = new Point(72, 214);
var point2 = new Point(205, 177);
...
var point7 = new Point(868, 177);
var myCircle1 = new Path.Circle(point1, 10)
var myCircle2 = new Path.Circle(point2, 10)
....
var myCircle7 = new Path.Circle(point7, 10)
var myPath = new Path([point1, point2, ..., point7]);

Safe getElementById or try to determine if ID exists in GUI

Method UiInstance.getElementById(ID) always returns GenericWidget object, even if ID does not exist.
Is there some way how to find out that returned object does not exist in my app, or check whether UI contains object with given ID?
Solution for UI created with GUI builder:
function getSafeElement(app, txtID) {
var elem = app.getElementById(txtID);
var bExists = elem != null && Object.keys(elem).length < 100;
return bExists ? elem : null;
}
It returns null if ID does not exist. I didn't test all widgets for keys length boundary, so be careful and test it with your GUI.
EDIT: This solution works only within doGet() function. It does not work in server handlers, so in this case use it in combination with #corey-g answer.
This will only work in the same execution that you created the widget in, and not in a subsequent event handler where you retrieve the widget, because in that case everything is a GenericWidget whether or not it exists.
You can see for yourself that the solution fails:
function doGet() {
var app = UiApp.createApplication();
app.add(app.createButton().setId("control").addClickHandler(
app.createServerHandler("clicked")));
app.add(app.createLabel(exists(app)));
return app;
}
function clicked() {
var app = UiApp.getActiveApplication();
app.add(app.createLabel(exists(app)));
return app;
}
function exists(app) {
var control = app.getElementById("control");
return control != null && Object.keys(control).length < 100;
}
The app will first print 'true', but on the click handler it will print 'false' for the same widget.
This is by design; a GenericWidget is a "pointer" of sorts to a widget in the browser. We don't keep track of what widgets you have created, to reduce data transfer and latency between the browser and your script (otherwise we'd have to send up a long list of what widgets exist on every event handler). You are supposed to keep track of what you've created and only "ask" for widgets that you already know exist (and that you already know the "real" type of).
If you really want to keep track of what widgets exist, you have two main options. The first is to log entries into ScriptDb as you create widgets, and then look them up afterwards. Something like this:
function doGet() {
var app = UiApp.createApplication();
var db = ScriptDb.getMyDb();
// You'd need to clear out old entries here... ignoring that for now
app.add(app.createButton().setId('foo')
.addClickHandler(app.createServerHandler("clicked")));
db.save({id: 'foo', type: 'button'});
app.add(app.createButton().setId('bar'));
db.save({id: 'bar', type: 'button'});
return app
}
Then in a handler you can look up what's there:
function clicked() {
var db = ScriptDb.getMyDb();
var widgets = db.query({}); // all widgets
var button = db.query({type: 'button'}); // all buttons
var foo = db.query({id: 'foo'}); // widget with id foo
}
Alternatively, you can do this purely in UiApp by making use of tags
function doGet() {
var app = UiApp.createApplication();
var root = app.createFlowPanel(); // need a root panel
// tag just needs to exist; value is irrelevant.
var button1 = app.createButton().setId('button1').setTag("");
var button2 = app.createButton().setId('button2').setTag("");
// Add root as a callback element to any server handler
// that needs to know if widgets exist
button1.addClickHandler(app.createServerHandler("clicked")
.addCallbackElement(root));
root.add(button1).add(button2);
app.add(root);
return app;
}
function clicked(e) {
throw "\n" +
"button1 " + (e.parameter["button1_tag"] === "") + "\n" +
"button2 " + (e.parameter["button2_tag"] === "") + "\n" +
"button3 " + (e.parameter["button3_tag"] === "");
}
This will throw:
button1 true
button2 true
button3 false
because buttons 1 and 2 exist but 3 doesn't. You can get fancier by storing the type in the tag, but this suffices to check for widget existence. It works because all children of the root get added as callback elements, and the tags for all callback elements are sent up with the handler. Note that this is as expensive as it sounds and for an app with a huge amount of widgets could potentially impact performance, although it's probably ok in many cases especially if you only add the root as a callback element to handlers that actually need to verify the existence of arbitrary widgets.
My initial solution is wrong, because it returns false exist controls.
A solution, based on Corey's answer, is to add the setTag("") method and here is ready to use code. It is suitable for event handlers only, because uses tags.
function doGet() {
var app = UiApp.createApplication();
var btn01 = app.createButton("control01").setId("control01").setTag("");
var btn02 = app.createButton("control02").setId("control02").setTag("");
var handler = app.createServerHandler("clicked");
handler.addCallbackElement(btn01);
handler.addCallbackElement(btn02);
btn01.addClickHandler(handler);
btn02.addClickHandler(handler);
app.add(btn01);
app.add(btn02);
return app;
}
function clicked(e) {
var app = UiApp.getActiveApplication();
app.add(app.createLabel("control01 - " + controlExists(e, "control01")));
app.add(app.createLabel("control02 - " + controlExists(e, "control02")));
app.add(app.createLabel("fake - " + controlExists(e, "fake")));
return app;
}
function controlExists(e, controlName) {
return e.parameter[controlName + "_tag"] != null;
}

AS3 Passing Variable Parameters to a generic Function Menu / SubItems

I'm no code genius, but a fan of action script.
Can you help me on this:
I have a function that depending on the object selected, will call event listeners to a set of 'sub-items' that are already on stage (I want to reuse this subitems with changed parameters upon click, instead of creating several instances and several code).
So for each selected 'case' I have to pass diferent variables to those 'sub-items', like this:
function fooMenu(event:MouseEvent):void {
switch (event.currentTarget.name)
{
case "btUa1" :
trace(event.currentTarget.name);
// a bunch of code goes here
//(just cleaned to easy the view)
/*
HELP HERE <--
here is a way to pass the variables to those subitems
*/
break;
}
}
function fooSub(event:MouseEvent):void
{
trace(event.target.data);
trace(event.currentTarget.name);
// HELP PLEASE <-> How can I access the variables that I need here ?
}
btUa1.addEventListener(MouseEvent.CLICK, fooMenu);
btUa2.addEventListener(MouseEvent.CLICK, fooMenu);
btTextos.addEventListener(MouseEvent.CLICK, fooSub);
btLegislacao.addEventListener(MouseEvent.CLICK, fooSub);
Anyone to help me please?
Thank very much in advance. :)
(I'm not sure I got your question right, and I haven't developed in AS3 for a while.)
If you want to simply create function with parameters which will be called upon a click (or other event) you can simply use this:
btUa1.addEventListener(MouseEvent.CLICK, function() {
fooMenu(parameters);
});
btUa2.addEventListener(MouseEvent.CLICK, function() {
fooMenu(other_parameters)
}):
public function fooMenu(...rest):void {
for(var i:uint = 0; i < rest.length; i++)
{
// creating elements
}
}
If you want to call event listeners assigned to something else you can use DispatchEvent
btnTextos.dispatchEvent(new MouseEvent(MouseEvent.CLICK))
Remember, you can't use btTextos.addEventListener(MouseEvent.CLICK, carregaConteudo("jocasta")); because the 2nd parameter you pass while adding Eventlistener will be considered as function itself - there are two proper ways to use addEventListener:
1:
function doSomething(event:MouseEvent):void
{
// function code
}
element.addEventListener(MouseEvent.CLICK, doSomething); //notice no brackets
2:
element.addEventListener(MouseEvent.CLICK, function() { // function code });
So:
function fooSub(event:MouseEvent, bla:String):void
{
trace(event.currentTarget.name+" - "+bla);
// bla would be a clip name.
}
codebtTextos.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) { fooSub(e, "jocasta") } );
Or try something like this if you want content to be dynamically generated:
btUa1.addEventListener(MouseEvent.CLICK, function() {
createMenu(1);
});
btUa2.addEventListener(MouseEvent.CLICK, function() {
createMenu(2);
});
function createMenu(id):void
{
// Switching submenu elements
switch (id)
{
case 1:
createSubmenu([myFunc1, myFunc2, myFunc3]); // dynamically creating submenus in case you need more of them than u already have
break;
case 2:
createSubmenu([myFunc4, myFunc5, myFunc6, myFunc7]);
break;
default:
[ and so on ..]
}
}
function createSubmenu(...rest):void {
for (var i:uint = 0; i < rest.length; i++)
{
var mc:SubItem = new SubItem(); // Subitem should be an MovieClip in library exported for ActionScript
mc.addEventListener(MouseEvent.CLICK, rest[i] as function)
mc.x = i * 100;
mc.y = 0;
this.addChild(mc);
}
}
Your question is rather vague; what "variables" do you want to "pass"? And what do you mean by "passing the variable to a sub item"? Usually "passing" means invoking a function.
If you can be more specific on what exactly your trying to do that would be helpful. In the meantime, here are three things that may get at what you want:
You can get any member of any object using bracket notation.
var mc:MovieClip = someMovieClip;
var xVal:Number = mc.x; // The obvious way
xVal = mc["x"]; // This works too
var propName:String = "x";
xVal = mc[propName] ; // So does this.
You can refer to functions using variables
function echo(message:String):void {
trace(message);
}
echo("Hello"); // The normal way
var f:Function = echo;
f("Hello"); // This also works
You can call a function with all the arguments in an array using function.apply
// Extending the above example...
var fArgs:Array = ["Hello"];
f.apply(fArgs); // This does the same thing
Between these three things (and the rest parameter noted by another poster) you can write some very flexible code. Dynamic code comes at a performance cost for sure, but as long as the frequency of calls is a few hundred times per second or less you'll never notice the difference.

JSLint writing constructors that reference static variables

I'm writing a display class in Javascript (using jQuery) which may be instantiated before a web page has loaded. If the page isn't ready when the constructor is called, the instance is added to a static instances field for the class, which is iterated over when the page has loaded:
function MemDisplay(ready_callback) {
this.readyCallback = ready_callback;
if (MemDisplay.ready) {
this.linkToPage();
} else {
MemDislay.instances.push(this);
}
}
//this makes sure that the ready callback can be sent when the page has loaded
MemDisplay.ready = false;
MemDisplay.instances = [];
$(document).ready(function () {
var i;
MemDisplay.ready = true;
for (i = 0; i < MemDisplay.instances.length; i += 1) {
MemDisplay.instances[i].linkToPage();
} });
//example truncated for brevity
When I run this through JSLint, I get this error:
Problem at line 25 character 9:
'MemDislay' is not defined.
MemDislay.instances.push(this);
I need to reference MemDisplay.instances in the constructor, but the constructor is where MemDisplay is defined, so I'm puzzled about how to make this work while fitting within JSLint's guidelines. Is there a better way to do this? Should I just ignore JSLint in this instance?
JSLint here is actually highlighting a broader issue with the code without saying so.
You are referencing a class (MemDisplay) but never instantiating it as an object. I.e. you are treating the class like an already-instantiated object.
I've created a very simple equivalent to what you are trying to achieve (also at this JSFiddle)
function MyClass(p1, p2){
this.param1 = p1; //class member/property - use this to access internally.
if (this.param1 === 1){ //you might want to consider doing this as part of some setter method
alert("test");
}
this.MyMethod = function(){ //class method/function
alert("MyMethod Called");
};
}
var myObj = new MyClass(1,2); //instantiate
alert(myObj.param1); //get value of object member (you can set as well)
myObj.MyMethod(); //call a method
It'll take a bit of reorgansiation, but by declaring the values up front, you can get make JSLint happy.
My brain must have figured this out while I slept: the trick is to attach the field to the prototype, which seems pretty obvious now that I've thought of it, since that's what you have to do to define class methods.
The following checks out in JSLint, and demonstrates the sharing of a field between all instances of MyClass (or see this code on jsfiddle):
/*global alert */
function MyClass(name) {
this.name = name;
MyClass.prototype.field += 1;
}
MyClass.prototype.field = 0;
MyClass.prototype.myMethod = function () {
alert(this.name + "'s class's field is " + MyClass.prototype.field);
};
var myObj = new MyClass("first");
myObj.myMethod();
var myOtherObj = new MyClass("second");
myObj.myMethod();
myOtherObj.myMethod();
I'm not sure if there's a prettier way to do it, as having 'prototype' all over the place feels a bit excessive, on the other hand it could be a good thing because it makes it clear that prototype.field does not belong to the instance.