Thursday, April 7, 2011

[YouTube-API] Traversal (bubbling) of AS Mouse Events through Chromeless Player

package
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.net.URLRequest;
import flash.system.Security;
import flash.system.SecurityDomain;
import flash.system.ApplicationDomain;

/**
* ...
* @author
*/
public class Main extends Sprite
{

public var mousePos:TextField;

public var player:Object;

public var loader:Loader;

public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);

}

private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point

drawBackground();

}

private function drawBackground():void
{
var bg:Sprite = new Sprite();
bg.graphics.beginFill(0xeeeeee);
bg.graphics.drawRect(0, 0, 800, 600);
this.addChild(bg);

addMousePosLabel();
}

private function addMousePosLabel():void
{

mousePos = new TextField();
mousePos.text = "initialized";
mousePos.x = 10;
mousePos.y = 10;

this.addChild(mousePos);

initializeTracking();
}

private function initializeTracking():void
{
this.addEventListener(MouseEvent.MOUSE_MOVE, updateMousePos);

initializeVideo();
}

private function updateMousePos(me:MouseEvent):void
{
var output:String = "x: " + this.mouseX + " | y: " + this.mouseY;

mousePos.text = output;
}

private function initializeVideo():void
{
Security.allowDomain("*");

var context:LoaderContext = new LoaderContext();
//context.securityDomain = SecurityDomain.currentDomain;
context.applicationDomain = new ApplicationDomain();

var url:URLRequest = new URLRequest("http://www.youtube.com/apiplayer?version=3");

loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);

loader.load(url,context);
}

private function onLoaderInit(event:Event):void {

loader.x = 10;
loader.y = 40;

this.addChild(loader);
loader.content.addEventListener("onReady", onPlayerReady);
loader.content.addEventListener("onError", onPlayerError);
loader.content.addEventListener("onStateChange", onPlayerStateChange);
loader.content.addEventListener("onPlaybackQualityChange",
onVideoPlaybackQualityChange);
}

private function onPlayerReady(event:Event):void {
// Event.data contains the event parameter, which is the Player API ID
trace("player ready:", Object(event).data);

// Once this event has been dispatched by the player, we can use
// cueVideoById, loadVideoById, cueVideoByUrl and loadVideoByUrl
// to load a particular YouTube video.
player = loader.content;
// Set appropriate player dimensions for your application
player.setSize(480, 360);
}

private function onPlayerError(event:Event):void {
// Event.data contains the event parameter, which is the error code
trace("player error:", Object(event).data);
}

private function onPlayerStateChange(event:Event):void {
// Event.data contains the event parameter, which is the new player state
trace("player state:", Object(event).data);
}

private function onVideoPlaybackQualityChange(event:Event):void {
// Event.data contains the event parameter, which is the new video quality
trace("video quality:", Object(event).data);
}

}

}
Hello everyone!


I bring to your attention what is in my opinion a pretty important bug: Mouse events of any kind are captured by the youtube player (chromeless or not) and are not bubbled up the display hierarchy in non-debug environments.

Searching this forum shows that this issue has already been brought up (here and here), but no clear answer was provided.

I've uploaded an example to illustrate what I'm saying: http://www.bauger.net/temp/ and I've attached the source to that example (main.as is enough).

I'll explain quickly what is done in this example:
  1. Stage is created and skinned
  2. an event listener is added to the stage to listen for MOUSE_MOVE events and output the global X and Y coordinates of the mouse
  3. a youtube video is loaded and added to display list following the official method detailed here
What is expected: the listener for MOUSE_MOVE events should fire everywhere on the stage, even when moving it over the youtube player. This should update the shown coordinates in the top left.

What happens: This works only when running the swf from the bin output folder. When publishing to the web or even running from another folder, the player keeps all mouse events from reaching the stage. This is what you can observe at the example url I have provided.

The Security.allowDomain(*) call ensures content from any URL is allowed, and I've tried setting the loaderContext's SecurityDomain and ApplicationDomain properties to all possible values but no dice; when the swf leaves the output folder it stops sharing mouse events.

Now, I think it would be pretty important to have a definitive answer for why the player behaves like this. Is it by design? Are you aware of the issue? I feel it's very important to get this working right, because this means that any app loading the player accepts having a "black hole" upon which it is impossible to sanely handle the mouse. This rules out dragging any object over the player because the dragged object stops at the boundary of the player. I believe this also forces many people wishing to provide a richer experience to put an overlay above the player, capturing mouse input before it reaches the player, which I believe is forbidden.

Is the engineering team aware of this? I would appreciate if you could look over the provided source and provide a solution for this problem.

Thanks,

Balthazar Auger

--
You received this message because you are subscribed to the Google Groups "YouTube APIs Developer Forum" group.
To post to this group, send email to youtube-api-gdata@googlegroups.com.
To unsubscribe from this group, send email to youtube-api-gdata+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/youtube-api-gdata?hl=en.

No comments:

Post a Comment