I am working in actionscript3, and since I'm self-taught, I think I've developed some bad habits, including coding on the timeline and using multiple scenes.
I am hoping to rectify this now that I'm working on a larger project.
Based on what I've read, linking multiple .fla files together is a better practice, each with their own document class. Is that correct?
If so, how do I load one .fla with its document class and then link that into the subsequent .fla file (instead of using scenes)? Or am I misinterpreting what was recommended?
Thanks!
There's no point to split your application in several loadable modules unless you have any of the following preconditions:
you have smart resource management to load and unload content
if you put everything into one file it gets just too big and hard to work with in design time or it takes far too long to compile
Regular AS3 alternative to working with scenes is creating/destroying content instances and using the main document class as their manager. You design content in the library and create behavior AS3 classes for them. Lets say, you have two content classes A and B. At the start the manager should show one of them and wait for the signal to show next one:
private var APage:A;
private var BPage:B;
gotoA();
function gotoA():void
{
if (BPage)
{
BPage.destroy();
removeChild(BPage);
BPage.removeEventListener(Event.CLOSE, gotoA);
}
APage = new A;
APage.addEventListener(Event.CLOSE, gotoB);
addChild(APage);
}
function gotoB():void
{
if (APage)
{
APage.destroy();
removeChild(APage);
APage.removeEventListener(Event.CLOSE, gotoB);
}
BPage = new B;
BPage.addEventListener(Event.CLOSE, gotoA);
addChild(BPage);
}
So, both A and B should have respective methods .destroy() that release used resources, unsubscribes methods from events, remove display objects, and so on, and they both should fire Event.CLOSE when they're done.
If you have many pages like that, you need to go for more algorithmic approach. For example, to create class BasicPage which will interact with manager and have the methods needed in all pages already declared:
package
{
import flash.display.Sprite;
class BasicPage extends Sprite
{
// A reference to the page manager instance.
public var Manager:PageManager;
public function destroy():void
{
while (numChildren > 0) removeChildAt(0);
Manager = null;
}
// Subclasses will have an access to this method to tell manager to show another page.
protected function showOtherPage(pageClass:Class):void
{
Manager.showPage(pageClass);
}
// A method that is called by manager when everything is ready.
// If page should take any actions on start it is a good idea to override this method.
public function startEngine():void
{
}
}
}
Then, example page A:
package
{
import flash.events.MouseEvent;
public class A extends BasicPage
{
// Lets say, class A in library have a designed button named Click.
public var Click:SimpleButton;
// We have things to undo here.
override public function destroy():void
{
Click.removeEventListener(MouseEvent.CLICK, onClick);
Click = null;
// Pass the destruction to superclass so it wraps its existence either.
super.destroy();
}
override public function startEngine():void
{
Click.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void
{
// Lets use inherited method to show other page.
showOtherPage(B);
}
}
}
So, PageManager will be like:
package
{
public class PageManager extends Sprite
{
private var Page:BasicPage;
// constructor
function PageManager()
{
super();
showPage(A);
}
function showPage(pageClass:Class):void
{
if (Page)
{
Page.destroy();
removeChild(Page);
Page = null;
}
Page = new pageClass;
Page.Manager = this;
addChild(Page);
Page.startEngine();
}
}
}
This all could look scary at first, but it really isn't. PageManager will always have a current page, once there's a need to show another page, the current will be destroyed on a regular basis. Each page class will tend to its own content, which makes coding simpler, for you don't need to see the whole picture. If you need any persistent data, keep it in the PageManager so each page will have access to the data with no need for the pages to communicate with each other.
So I'm reading a book about MVC and the author create a controller (PlayerController) and put some functions for keyboard use and some for mouse use. But he just comment out every keyboard use.
It gave me a idea to create 2 controllers, PlayerMouseController and PlayerKeyboardController so I can decide how to control the player changing one line. And if I can design this way, later I can add a AIController for monsters that use the same view and model but are controlled by AI and so on...
I have my model Player and it do the physics stuff. Now I want two controller, one for mouse and other for keyboard. So I create a PlayerMouseController and PlayerKeyboardController.
The PlayerMouseController has 2 functions: processUpdate() and processMouseDown()
The PlayerKeyboardController has 2 functions: processKeyDown() and processKeyUp()
I create the object like this:
_player = new Player();
_playerController = new PlayerMouseController(_player);
_playerView = new PlayerView(_player, _playerController, stage);
addChild(_playerView);
If I want to change the controller I can just change the _playerController line for this:
_playerController = new PlayerKeyboardController(_player);
And it works fine... But I dont know if the design I use is fine for a large project
To make this work I have to create a Controller class with nothing so I can extends the others controllers and my view can call all methods.
public class Controller
{
public function processKeyDown(e:KeyboardEvent):void
{
}
public function processKeyUp(e:KeyboardEvent):void
{
}
public function processUpdate(stage:Stage):void
{
}
public function processMouseDown(e:MouseEvent):void
{
}
}
In my view (PlayerView) I accept any Controller:
public function PlayerView(model:Player, controller:Controller, stage:Stage)
{
_model = model;
_controller = controller;
_stage = stage;
}
and I decide what to use based on its type:
if (_controller is PlayerKeyboardController)
{
_stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
_stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
else if (_controller is PlayerMouseController)
{
_model.addEventListener(Model.UPDATE, onUpdate);
_stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
// EVENTS PlayerKeyboardController
private function onKeyDown(e:KeyboardEvent):void
{
_controller.processKeyDown(e);
}
private function onKeyUp(e:KeyboardEvent):void
{
_controller.processKeyUp(e);
}
// EVENTS PlayerMouseController
private function onUpdate(e:Event):void
{
_controller.processUpdate(_stage);
}
private function onMouseDown(e:MouseEvent):void
{
_controller.processMouseDown(e);
}
So... this is a good idea? How should I improve??
I think as your project evolves you'll soon hit a bottleneck with this kind of approach. I'd suggest creating a general IEntityController interface (don't be afraid of the word 'entity', it just shows that this is a game object controller, something that is parent for example of hero, enemy, etc.).
Then I'd create two separate implementations of this interface: BasicMouseController and BasicKeyboardController, so that I'd have two branches of these controllers with basic logic. If you need additional functionality for the Hero, you'd create a HeroMouseController class which would extend the BasicMouseController class and would have the advantage of calling super methods and adding the extended functionality easily.
You'd still have the benefit of passing different controllers to the PlayerView class as it's constructor would receive an IEntityController as a parameter, meaning anything implementing this class can be passed in.
There are many approaches for problems like this and StackOverflow is usually not meant to give these kind of answers, as every answer to these kind of questions is subjective and this website more fit for Problem/Solution kind of posts.
I'm writing a game where you have to go through a maze. I want this game to have different levels. But for each level, the maze is going to be different. So I drew other walls. But I do not want to write my collision detection method 50 times if I have 50 different levels.
I thought of a way of fixing it, but it's not working. I created a new symbol with nothing in it and named it wall. I think that I can make my wall = wall1 (another symbol I converted, and exported for as), and just do stage.addChild(wall). But I can't find a way to do that. So I need help!
Make a generic class e.g. Wall and make your library symbols use that for their base class. You won't need to create them at runtime using ActionScript for this inheritance to work, you can still just place your MovieClips on the stage.
The next thing you need to do is store these Walls somewhere. Because you seem inexperienced with ActionScript, and want to avoid writing code for new levels, you can automate this process using a manager type class. We will call this class WallManager and it will look like this:
public class WallManager
{
private static var _walls:Vector.<Wall> = new <Wall>[];
internal static function register(wall:Wall):void
{
_walls.push(wall);
}
public static function reset():void
{
_walls = new <Wall>[];
}
public static function get walls():Vector.<Wall>{ return _walls; }
}
Then we'll create your Wall class. Within the constructor for this class, we will automatically have the Wall add itself into the WallManager listing:
public class Wall extends Sprite
{
public function Wall()
{
WallManager.register(this);
}
public function touchingMouse(mouseX:int, mouseY:int):Boolean
{
// For this example I am checking for collisions with the
// mouse pointer. Replace this function with your own collision
// logic for whatever it is that is supposed to collide with
// these walls.
if(parent === null) return false;
var bounds:Rectangle = getBounds(parent);
return bounds.contains(mouseX, mouseY);
}
}
This setup is not 'best practice', but it is suitable in your situation because your project seems small, you appear to be working on it alone, it's simple and it gets the job done.
At the end of each level, use WallManager.reset() to remove the walls from the previous level. For checking collisions across all walls, just use a loop like this:
for each(var i:Wall in WallManager.walls)
{
var collision:Boolean = i.touchingMouse(mouseX, mouseY);
if(collision)
{
// There was a collision.
//
//
}
}
You can make one MovieClip with 50 frames saying stop() on the first frame and do your code like this:
private var wallnum:int;
public function Main()
{
stop();
wallnum = 1;
var wallobj = new Wall();
addChild(wallobj);
wallobj.gotoAndStop(wallnum);
}
For collision detection, I recommend Pixel Perfect Collision Detection (https://code.google.com/p/master-air-controller/source/browse/trunk/master-air-controller/src/PixelPerfectCollisionDetection.as?spec=svn6&r=6)
just thought I would share something I have found to help delivering data across an application I am wondering what others think about this I wanted to have a way to capture event bubbling up back down to other components but in a way that it would make it easy to use anywhere in may app so this is what i came up with.
I Extend the Application class and wrap in an abstract function registering a function of any component anywhere and capture it at the top most level and pass to where ever i chose to.
public class AxApplication extends Application
{
public var ___registeredEvents:Array = new Array();
public var ___registeredFunctions:Array = new Array();
function AxApplication()
{
super();
}
public function localRegisterForEvent(e:Event,func:*,caller:*):void
{
caller.addEventListener(e.type,localCallerEventHandler,true,3);
caller.addEventListener(e.type,localCallerEventHandler,false,3);
___registeredEvents.push(e);
___registeredFunctions.push(func);
}
public function localCallerEventHandler(e:*):void
{
if(e!=null)
{
for(var i:int = 0 ; i< ___registeredEvents.length; i++)
{
if(e.type == ___registeredEvents[i].type)
{
___registeredFunctions[i](e);
//the registered function gets called
//there no garbage collection implemented!
}
}
}
}
}
I think that is not a very useful solution. Why? Because you scatter AxApplication references around the application. Views and Model instance don't need any references to the application at all. It would be better to to implement a controller layer which uses a simple eventBus property, which could look like:
private static const _EVENT_BUS:IEventDispatcher = FlexGlobals.topLevelApplication;
protected final function eventBus():IEventDispatcher {
return _EVENT_BUS;
}
If you implement a base view controller/mediator (depending from which framework you're coming), you don't have any reference to non-framework classes at all, which makes it highly reusable. It is just a simple reuse of the Application singleton which you use to dispatch system wide events. You register listeners in the view controller/mediator and update the views or models accordingly. RobotLegs for example uses a system wide event dispatcher as well.
Why not just using the parentApplication approach? Because you can't implement tests (the generated test-runner of IDEs won't extend your AxApplication) or just yank the components/models in a different application - that is basically not possible.
I am using the JWPlayer source code for 6.0.2813 (http://developer.longtailvideo.com/trac/) and It seems the even though I have a movieclip and I added the jwplayer class as a child, the jwplayer creates itself as a child of the main stage, thus allowing it to expand to the bound of the stage and not my movieclip (which I want to be a resizeable/draggable container) in my flash.
I asked the forums for help but they said they never intended it this way and wasn't much help. I was hoping someone familar with the source code could point my in the right direction.
How can I get the JWPlayer to be contained to a movieclip?
Edit:
I made a little bit of progress.
I found the RootReference class in com/longtailvideo/jwplayer/utils/RootReference.as
public function RootReference(displayObj:DisplayObject) {
if (!RootReference.root) {
RootReference.root = displayObj.root;
RootReference.stage = displayObj.stage;
try {
Security.allowDomain("*");
} catch(e:Error) {
// This may not work in the AIR testing suite
}
}
}
And noticed that the RootReference.stage is where things get added as a child. RootReference.stage = displayObj.stage; where the player class object is sent as displayObj I changed it to be RootReference.stage = MovieClip(root).gui.video_container;
Then throughout the code RootReference.stage.stageHeight and RootReference.stage.stageWidth was used so I switched it to RootReference.stage.height and RootReference.stage.width. This got it to compile and now the video is within the container but the video's top left is center on my video_container's center and the video isn't resized to the size of my container, but rather the size of the video. Also the controls are completely messed up.
But I was able to resize and move the video around
Assuming my testing scenarios are representative of your use-cases, I think I managed to hack up a work around.
The gist of the approach is to replace RootReference.root and RootReference.stage with a fake stage object that you control. Because most of the jwPlayer classes refer to those static variables instead of their own root and stage variables, this seems to work, for the most part. What ended up being the most complicated issue was working with the Stage.stageVideo objects, which I think are the hardware accelerated video objects. These are always attached to the stage and thus weren't compatible with the fake stage object. The main issue with those is positioning, and I have it mostly worked out, but there is still one glitch which I'll describe later but it should be ok, now.
The jwPlayer embed script was causing a lot of problems, so to get started I switched to normal SWFObject-based embedding and added a javascript function to the page called getFlashvars() that returned the configuration settings. Then, I changed the com.longtailvideo.jwplayer.utils.Configger.loadExternal() method to the following:
private function loadExternal():void {
if (ExternalInterface.available) {
try {
//var flashvars:Object = ExternalInterface.call("jwplayer.embed.flash.getVars", ExternalInterface.objectID);
var flashvars:Object = ExternalInterface.call("getFlashvars");
if (flashvars !== null) {
// TODO: add ability to pass in JSON directly instead of going to/from a string
for (var param:String in flashvars) {
setConfigParam(param, flashvars[param]);
}
dispatchEvent(new Event(Event.COMPLETE));
return;
}
} catch (e:Error) {}
}
}
That's something you probably don't have to deal with per not using a webpage.
The fake stage class is called StageInterceptor and is a singleton. To apply it, there were minor changes in the RootReference class:
package com.longtailvideo.jwplayer.utils {
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.system.Security;
// added --------
import somePackage.StageInterceptor;
/**
* Maintains a static reference to the stage and root of the application.
*
* #author Pablo Schklowsky
*/
/* Modified for a stackoverflow question: http://stackoverflow.com/questions/13325318/jwplayer-trying-to-bound-the-video-player-inside-my-own-container */
public class RootReference {
/** The root DisplayObject of the application. **/
public static var root:DisplayObject;
// altered --------
/** A reference to the stage. **/
private static var _stage:StageInterceptor;
// altered --------
public static function get stage():StageInterceptor {
return _stage;
}
public function RootReference(displayObj:DisplayObject) {
if (!RootReference.root) {
// altered --------
RootReference.root = StageInterceptor.singleton;
RootReference._stage = StageInterceptor.singleton;
try {
Security.allowDomain("*");
} catch(e:Error) {
// This may not work in the AIR testing suite
}
}
}
}
}
Also, I removed the set stage() setter method from the class.
In the document class, I have the following code. The MouseEvent.CLICK handler is to test positioning and re-sizing the movie. The only thing you really need are the first few lines:
// add StageInterceptor to the display tree
addChild(StageInterceptor.singleton);
// add the jwPlayer:
var p:Player = new Player();
StageInterceptor.singleton.addChild(p);
// for testing only:
stage.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
var stg:StageInterceptor = StageInterceptor.singleton;
if (e.altKey) {
// click + alt: ignored (so can play, etc)
return;
} else if (e.shiftKey) {
// click + shift: resizes
stg.width = e.stageX - stg.x;
stg.height = e.stageY - stg.y;
} else {
// click: moves video
stg.x = e.stageX;
stg.y = e.stageY;
}
});
I put StageInterceptor in the package somePackage. It looks like this:
package somePackage
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.InteractiveObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.StageVideo;
public class StageInterceptor extends Sprite
{
private static var _singleton:StageInterceptor = new StageInterceptor();
public static function get singleton():StageInterceptor {
return _singleton;
}
private var _bg:Bitmap;
public function StageInterceptor()
{
super();
scrollRect = new Rectangle(0, 0, 500, 500);
var bmpData:BitmapData = new BitmapData(500, 500, false, 0);
_bg = new Bitmap(bmpData);
_bg.alpha = 0.1;
_bg.cacheAsBitmap = true;
addChild(_bg);
if (stage) {
initOnStage();
} else {
addEventListener(Event.ADDED_TO_STAGE, initOnStage);
}
}
private function initOnStage(e:Event = null):void {
if (e) {
removeEventListener(Event.ADDED_TO_STAGE, initOnStage);
}
stage.addEventListener(Event.RESIZE, onStageResized);
}
private function onStageResized(e:Event):void {
e.stopImmediatePropagation();
dispatchEvent(new Event(Event.RESIZE));
updateStageVids();
}
public function updateStageVids():void {
if (stage.stageVideos.length > 0) {
for each (var sv:StageVideo in stage.stageVideos) {
if (!sv.videoWidth || !sv.videoHeight) {
continue;
}
var rect:Rectangle = stretch(sv.videoWidth, sv.videoHeight, width, height);
rect.x = Math.max(0, x + 0.5 * (width - rect.width))
rect.y = Math.max(0, y + 0.5 * (height - rect.height));
sv.viewPort = rect;
}
}
}
override public function get width():Number {
return scrollRect.width;
}
override public function set width(value:Number):void {
if (value != width) {
_bg.width = value;
scrollRect = new Rectangle(0, 0, value, scrollRect.height);
dispatchEvent(new Event(Event.RESIZE));
updateStageVids();
}
}
override public function set height(value:Number):void {
if (value != height) {
_bg.height = value;
scrollRect = new Rectangle(0, 0, scrollRect.width, value);
dispatchEvent(new Event(Event.RESIZE));
updateStageVids();
}
}
override public function get height():Number {
return scrollRect.height;
}
public function get stageWidth():Number {
return scrollRect.width;
}
public function get stageHeight():Number {
return scrollRect.height;
}
public function get scaleMode():String {
return stage.scaleMode;
}
public function set scaleMode(value:String):void {
stage.scaleMode = value;
}
public function get displayState():String {
return stage.displayState;
}
public function set displayState(value:String):void {
stage.displayState = value;
}
public function get focus():InteractiveObject {
return stage.focus;
}
public function set focus(value:InteractiveObject):void {
stage.focus = value;
}
public function get stageVideos():* {
return stage.stageVideos;
}
override public function set x(value:Number):void {
if (value != x) {
super.x = value;
updateStageVids();
}
}
override public function set y(value:Number):void {
if (value != y) {
super.y = value;
updateStageVids();
}
}
/**
* Copied from com.longtailvideo.jwplayer.utils.Stretcher, modified to only
* do 'uniform' stretch and to return a Rectangle class.
**/
public static function stretch(elmW:Number, elmH:Number, availW:Number, availH:Number):Rectangle {
var scale:Number = Math.min(availW / elmW, availH / elmH);
elmW = Math.round(elmW * scale);
elmH = Math.round(elmH * scale);
return new Rectangle(0, 0, elmW, elmH);
}
}
}
The issue that remains has to do with the positioning of video instances when they are initialized. I think simply calling StageInterceptor.singleton.updateStageVids(); at the right point will do the trick, but I'm not sure. The edit below covers how this was addressed.
I'm not sure how well this will work if you're not using stageVideo. But, with any luck, this will move things in the right direction.
Edit:
I've updated the StageInterceptor class to do a better job scaling and positioning the video.
Also, it looks like the initial position of videos (at least when it's a stageVideo, is that what you're using?) can be corrected by a small edit in the com.longtailvideo.jwplayer.media.VideoMediaProvider class. Adding import somePackage.StageInterceptor; to the import statements at the top and then replacing this line (link to source):
_stage.viewPort = new Rectangle(_media.x,_media.y,_media.width,_media.height);
To:
StageInterceptor.singleton.updateStageVids();
So the method looks like:
/** Resize the video or stage.**/
override public function resize(width:Number, height:Number):void {
if(_media) {
Stretcher.stretch(_media, width, height, _config.stretching);
if (_stage) {
//_stage.viewPort = new Rectangle(_media.x,_media.y,_media.width,_media.height);
StageInterceptor.singleton.updateStageVids();
}
}
}
This should do the trick, but I haven't tested it for non stageVideos. And, this update also assumes you're playing videos progressively, not using RTMP media.
Edit:
To enable moving and resizing the player with non-StageVideo videos, but still progressively loaded, the contents of the com.longtailvideo.jwplayer.view.View.resizeMasker() method need to be either commented out or deleted:
protected function resizeMasker():void {
/*
if (_displayMasker == null)
setupDisplayMask();
_displayMasker.graphics.clear();
_displayMasker.graphics.beginFill(0, 1);
_displayMasker.graphics.drawRect(_components.display.x, _components.display.y, _player.config.width, _player.config.height);
_displayMasker.graphics.endFill();
*/
}
I also want to mention the open source version of the jwPlayer is governed under the Creative Commons license, as noted on their site:
JW Player 6 — Open Source Edition
The use of the JW Player Open Source edition is governed by a Creative Commons license. In short:
JW Player Open Source - You can use, modify, copy, and distribute this edition as long as it's for non-commercial use, you provide attribution, and share under a similar license.
The license summary and full text can be found here: CC BY-NC-SA 3.0
I'll address the jwPlayer portion of your question since flash is not my forte.
The problem here is jwPlayer is not a simple flash player, but also a HTML5 Multimedia Player as well.
Solution 1: SWFObject embedded in your Flash Object
Since jwPlayer is already compatible with SWFObject, use that (i.e., swfobject.js) as a mediator to load the player.swf file (aka jwPlayer) into your flash stage. To be sure, SWFObject acts as a container and will be the "bounded item" in your stage, as opposed to using jwPlayer directly.
Here is an online demo illustrating this solution except it's using a simple flash video player.
Flash Web Site with embedded SWFObject Logo Playing Music Video
Flash Web Site Documentation on swfObject
Note the HTML source page for that flash website shows RockOnFlashLogo.swf as the file being shown in the browsers entire viewport. Looking deeper, it's written in AS3.
Unlike jwPlayer v4 where many ideas floated on the internet to embed that version into flash websites due to its lax security, I think you'll have issues with current jwPlayer license checking, webpage Ready Event Listeners, and popular plugin integration... not to mention issues that may arise from streaming video content.
IMHO, the newer jwPlayer API and Player are intended to be use via webpage installation.
Solution 2: SWFObject On-Top of your Flash Object
This method treats jwPlayer how it was meant to be used; as a webpage installation. My motivation came from this online demo for Google Chrome Experiment | The Wilderness Downtown.
That demo places strategically synchronized browser windows on-top of the main browser window. Although the main browser window is in charge, all windows make up the entire experience. The same method, but flash flavored, can be done with your current project with excellent results.
Your Flash Object is in charge and contains within an interactive frame that is allocated for the jwPlayer webpage component. To be sure, this interactive frame for jwPlayer can accept relocation (e.g., via dragging frame edges) and resize (e.g, frame has resize-icon bottom-right) which is then relayed to the webpage component (i.e., player.swf) via SWFObject standard embed techniques (i.e., location and size set with jQuery).
Here's a basic cross-section of a webpage for three layered items:
The black layer is the HTML of the webpage. The red layer is your flash object which contains also has the built-in interactive frame shown in aqua. The green top layer is the SWFObject for jwPlayer's player.swf file.
Here's how it would work from jwPlayer to your flash object:
1. Webpage jwPlayer API has event listener active and can accept JavaScript.
2. Then jwPlayer API receives 'play' status from player, updates player event status.
3. Player Event Status is true for Play, and therefore triggers conditional if statement.
4. That if statement then transmits JavaScript to your flash object, indicating play mode is true.
5. Your flash object receives this JavaScript of play event, and dims the stage, less the aqua frame.
Here's how it would work from your flash object to jwPlayer:
1. The aqua frame has user interaction when it's moved to left side of stage.
2. Your AS3 code moves the aqua frame accordingly, while sending out JavaScript of that location.
3. Webpage receives JavaScript and invokes function to position jwPlayer player at new location.
Tip: If using a custom jwPlayer Skin, incorporate that skin theme into your flash object for a uniform look.
The benefit of this scenario is that jwPlayer maintains 100% integrity while both these flash objects work in tandem on your webpage. No hacks, no breakage, no unforeseen consequences, and no headaches... it's standard jwPlayer API and AS3 markup in use.
Hovering over the jwPlayer itself is 100% jwPlayer, while being bound to your flash object indirectly.
Per your written comments that JW Player will not have any access to JavaScript and will be using Firefox Source that is a specialized 3D Game/Chat Engine without access to any DOM elements outside the player, the best solution is to use JW Player Enterprise Edition.
That solution will put you in touch with the Marketing & Engineering department which can provide a turnkey solution to have JW Player integrated into your own product.
Click the image below which includes Licensing information as well: