My simple ActionScript
I am trying to use Flash's ExternalInterface to setup a callback so that JavaScript can call a method on my Flash object. Everything works fine in Safari, Firefox and in IE, but I cannot get Chrome working. When I try the code on Chrome, I get the following error:
Uncaught TypeError: Object #<an
HTMLObjectElement> has no method
'setText'
Here is the example HTML I am using (again, works fine in Safari, FF and IE)
<html><body>
<div id="mycontent"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<script type="text/javascript">
swfobject.embedSWF("http://invincible.dynalias.com:8080/HelloWorld.swf", "mycontent", "400", "420", "9.0.0","expressInstall.swf", {}, {allowScriptAccess:'always'},{id:'hw',name:'hw'});
function getFlash(movieName) {
return ( navigator.appName.indexOf("Microsoft") != -1) ? window[movieName] : document.getElementById(movieName);
}
</script><p>
<input type="text" id="exampleText" /> <input type="button" value="Set Text" onclick="getFlash('hw').setText(document.getElementById('exampleText')
.value)" />
</body>
</html>
and here is the ActionScript...
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.external.ExternalInterface;
import flash.system.Security;
public class HelloWorld extends Sprite {
private var textField:TextField = new TextField();
public function HelloWorld() {
Security.allowDomain("*");
ExternalInterface.addCallback("setText", this.setText);
textField.text = "Hello, world!";
addChild(textField);
}
public function setText(text:String):void {
this.textField.text = text;
}
}
}
I agree with Robson that it is a race condition, but it's not in 'writing the Flash tag' and adding a timer is not a good solution - in fact its very dangerous.
The problem is that the SWF itself isn't loaded and had a chance to initialize your external interface. For a small SWF in Chrome the timing may be more sensitive than other browers, but the underlying problem isn't specific to Chrome.
What you need to do is this :
In Actionscript
Call this function from your constructor :
public function InitializeExternalInterface():void
{
if (ExternalInterface.available) {
// register actionscript functions so they can be called by JS
ExternalInterface.addCallback("activate", activate);
Security.allowDomain("www.example.com");
// send message to parent page that SWF is loaded and interface active
trace("External Interface Initialized...");
ExternalInterface.call("flashInitialized")
}
else
{
trace("ERROR: External Interface COULD NOT BE Initialized...");
}
}
In your HTML
<script>
function flashInitialized()
{
alert("Initialized!"); // remove this obviously!
$('#Main')[0].activate(); // safe to call Flash now
}
</script>
You may find on your local machine that it works without this, but as soon as you add network delays into the equation you'll regret not doing this. An arbitrary timer is a bad idea because you will still get the error on a slow connection. This method lets the page call the flash object at the earliest possible time.
Note: Using jQuery's 'on ready' pattern is NOT a solution to the problem - although at first I mistook it for one.
$(function()
{
$('#animation')[0].SetTitle("Hello");
}
Also swfobject's callbackFn is also not a solution becasue that just tells you when the tag is inserted and not when the SWF is loaded.
I got the same problem, to fire and recieve listener events between javascript and flash.
The solution was to use AC_OETags.js file from Adobe as embedd script instead of JQuery flash. (It is found in the zip file under Client Side detection, Adobe probably have it some other places as well)
The trouble based on a race condition when the flash builds the javascript callbacks in the browser. This is not handeled correctly by a straight embed for some reason.
<div>
<script>
// Major version of Flash required
var requiredMajorVersion = 10;
// Minor version of Flash required
var requiredMinorVersion = 0;
var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
AC_FL_RunContent(
"src", "tagflash",
"width", "200",
"height", "200",
"id", "myTagFlash",
"quality", "high",
"bgcolor", "#FFFFFF",
"name", "myTagFlash",
"allowScriptAccess","always",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer",
"flashvars", "templateData=theYear:2010&theTagNumber:123"
);
</script>
</div>
Then you can do: (works in IE, FF, Safari, Crome,++)
$("#tagFlash").gotoNewFrame();
I was having problems with ExternalInterface and Firefox and Chrome and discovered that the Adobe Script was not writing the Flash tag quickly enough, so when the browser tried to find the addCallback() function it was not there at the time.
Simply putting my Javascript function that calls the Flash created addCallback() in a window.setTimeout() calling solves the problem. Delays less than 200 ms still make the problem to occur.
I didn’t have to use the solution of trying to find if the “length” attribute exists in the document[FlashId] object. Just calling “FlashEmbed = document[FlashId]” worked just fine.
After struggling a lot, I finally decided to use the official solution from Adobe:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html
Search for ExternalInterfaceExample.as.
There is a workaround to the problem by disabling Chrome built-in flash plugin:
type the chrome://plugins in the address bar of chrome.
expand the details of plugins by clicking the details on top right corner.
in the entry of "Adobe Flash Player", disabling the first one.
This is not a solution, but shows why this happens on Chrome. Chrome accompany with a built-in flash plugins, that often cause the troubles when we use the ExternalInterface of AS3, it's annoying.
Related
I'm trying to use a facebook UI request dialog for selecting a friend. This works absolutely fine in safari and Chrome but in firefox and IE11 (Not tested lower versions yet) it continuously hangs with the loading animation.
function pickFriend(ev)
{
FB.ui(
{
method: "apprequests",
message: "Choose a friend.",
max_recipients: 1,
title:"Invite a friend"
},sendMessage);
ev.preventDefault();
}
$("#element").click(pickFriend);
I then tried calling the function directly in the console to ensure that it wasn't my implementation that was the problem, and i got the same result with it hanging with the loading animation. I then tried different display options and i can get it too work in popup mode but for me this is not very elegant and i would far prefer it to work in iframe mode the same way it does in safari and chrome.
Has anyone else been experiencing this issue? If so is there a reason for this and is ther a fix?
I'm thinking that this maybe something that is entirely down to facebook to fix which would leave no other option but to run in popup mode if i want to keep browser compatibility.
In IE11 (and node) the javascript engine can get hung up on long operations. The recommended work around is utilizing "setImmediate", so for your example:
function pickFriend(ev)
{
ev.preventDefault();
setImmediate(FB.ui
, {
method: "apprequests",
message: "Choose a friend.",
max_recipients: 1,
title:"Invite a friend"
}
, sendMessage
);
}
$("#element").click(pickFriend);
Also, make sure your "sendMessage" variable is scoped properly and not undefined or null.
Syntax
var immediateID = setImmediate(func, [param1, param2, ...]);
var immediateID = setImmediate(func);
Reference:
https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate
I am aware of two ways of calling the "print" dialog of browser (I used the Search, of course):
document.print()
document.execCommand('print', false, null)
What is the difference between them? Support across browsers? Papers, docs or standards? Which is more correct thing to use?
Another question: what is the most straight way to print given part of a webpage? I know we can create new window or iframe to call any of two print methods above. Which one has less pitfalls?
I've tested different ways of printing part of webpage across browsers:
Chrome, Firefox, Opera (12 and new), IE11, 10, 9 and 8. I've tried to create new window, new iframe, or use existing iframe on the page. And then tried .print() and .execCommand('print').
Note: Keep in mind that .print() is called on window, and .execCommand() is called on document.
Code used for testing can be found here
Correct me if my testing code is wrong, I just wanted to find the clearest way to do the job. My conclusions:
Opera 12 can not print part of a webpage (?)
IEs don't print() iframes and windows, except current window.
Calling print() on documents inside iframes or created windows in IEs breaks the print() on current document. Be careful!
jQuery plugin printThis uses tricks for IE to do the job, and it just works. The only exception is Opera 12. By the way, this plugin uses print().
execCommand('print') works almost everywhere and with any approach (iframes, window). It's not supported by Firefox though.
execCommand() returns false if call was unsuccessful, so if you don't want to use plugins and magic tricks, create window or iframe, call execCommand('print') and if it returns false, call print().
One more thing:
Creating an iframe is tricky: you can't access its window or document directly (yes, you have ContentDocument property, which behaves differently across browsers). You should name it and then call window.frames[name] to get window object from that iframe. Do not try to call window.frames(id) - it will return the iframe.
That last method mentioned in the accepted answer, then, ends up looking like this:
iframe = document.getElementById('iframe-id');
var printed = iframe.contentWindow.document.execCommand('print', false, null);
if (!printed) window.print();
alternative:
try {
iframe = document.getElementById('iframe-id');
iframe.contentWindow.document.execCommand('print', false, null);
}
catch(e) {
window.print();
}
similar method used by printThis
if (document.queryCommandSupported("print")) {
$iframe[0].contentWindow.print();
$iframe[0].contentWindow.document.execCommand("print", false, null);
} else {
$iframe[0].contentWindow.focus();
$iframe[0].contentWindow.print();
}
You can use the combination of window.open and execComand (saveas
exemple:
<script type= "text/javascript">
function saveas() {
var oPrntWin = window.open("","_blank","width=1,height=1,left=1,top=1,menubar=yes,toolbar=yes,resizable=yes,location=no,scrollbars=yes");
oPrntWin.document.open();
oPrntWin.document.write(editeur.innerHTML);
oPrntWin .document.execCommand("SaveAs",true,"C:\\My Documents\\Saved Content.html");
oPrntWin.document.close();
}
</script>
editeur.html is a part of my document
you can do same for your frame
replace the writting in a new Window by the property "src" for the body
my first time on here.
My problem is with AS3, Javascript and possibly the browsers Firefox and IE.
I have done so much searching for an answer so i will print my code:
i am using this line to call the flash application and in all browsers its combatible and actually traces in firebug to hold an OBJECT->FLASH_ID so thats not the problem.
var obj = document.getElementById('test');
then i use addcallback:
obj.sendStatus(loggedIn);
now whats weird is that i trace all individual elments in chrome and
-obj = flash object
-sendStatus = flash->function
-loggedIn = either false or true;
everything works great but when i am on firefox or ie
it traces differently
-obj = flash object
-sendStatus = undefined
-loggedIn = either true or false;
now what am i missing??????????
i tried embedding rather than object insertion
i made sure that the id's were all unique
i checked to make sure i had the right flash object selected with getElementById
im so confused.. and it feels like something simple.
I know about some browser - dependent timing problems, making the interface of the flash object available...
A timer could help, try this:
var obj = document.getElementById('test');
setTimeout(function(){obj.sendStatus(loggedIn);}, 500);
500 is a bit to long, but just to be sure. If it works you can try to lower it to 200 - 300.
make sure you declared allowScriptAccess = sameDomain both in embed tag and object tag
in case you don't use swfObject
Maybe the way you get a reference to the swf is wrong, try this
function thisMovie(movieName) {
if (navigator.appName.indexOf("Microsoft") != -1) {
return window[movieName];
} else {
return document[movieName];
}
}
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html
The problem is that using ExternalInterface requires both parties (browser and flash) to be ready.
You can have the flash poll a method in the page which just returns true so that you know its ready to receive calls from flash.
On the flip side if the page is cached, it can sometimes happen that the page wants to send to flash before flash is ready, so I use a callback to the page telling it flash is ready, so its like a handshake, once both parties are ready, then we can start sending data back and forth.
This has been my approach since Firefox 3.
I'm trying to find a way to detect changes on a page from a BHO. For the Firefox version of the plugin I'm using DomNodeInserted, but this isn't available in IE. I've also looked at using onpropertychange, but this only allows you to monitor a single element (not children).
I'm now wondering if it's possible to monitor when AJAX requests are called. If so, I can make the changes to the page after the AJAX request has completed. Only problem is, I can't find a way to do it.
Here's my attempt so far, based on jeffamaphone's suggestions, it doesn't work but maybe it'll jog someone's memory.
public class ChangeMonitor : IHTMLChangeSink {
public void Notify()
{
Trace.WriteLine("notified");
}
}
void registerMonitor()
{
HTMLDocument document = _webBrowser2.Document;
ChangeMonitor monitor = new ChangeMonitor();
IHTMLChangeSink changeSink = monitor;
IHTMLChangeLog changeLog = null;
((IMarkupContainer2)document).CreateChangeLog(changeSink, out changeLog, 1, 1);
}
For IE, onreadystatechange will work as an equivalent to DomNodeInserted. A DHTML behavior must be attached to the element via htc, but the htc file does not have to exist:
document.documentElement.addBehavior("foo.htc");
document.documentElement.attachEvent("onreadystatechange", Notify);
The BHO can inject the script to handle the event.
I've never done it, but my theory is you can use IMarkupContainer2::CreateChangeLog().
Take a look at this code. It actually loads the video perfectly, but it doesn't fire onPlayerStateChange when pausing, loading, or playing the video.
I've already implemented a function to print any new video status, but it doesn't even report events.
Why isn't the onPlayerStateChange firing?
Code from JSBin:
<body>
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
google.load("swfobject", "2.1");
</script>
<script>
function onYouTubePlayerReady(playerId) {
var myplayer = document.getElementById("myplayer");
myplayer.cueVideoById("pGBMFxN_eys",0, "default");
myplayer.addEventListener("onStateChange", onPlayerStateChange);
}
function onPlayerStateChange(newState){
log(newstate);
}
function log(text){
document.getElementById('logarea').innerHTML+=text+"<br/>";
console.log(text);
}
function setup(){
var params = { allowScriptAccess: "always",wmode:"transparent" };
var atts = { id: "myplayer" };
swfobject.embedSWF("http://www.youtube.com/apiplayer?" +
"&enablejsapi=1&playerapiid=mojplayer",
"greatvideo", "400", "300", "8", null, null, params, atts);
}
google.setOnLoadCallback(setup);
</script>
<div id="logarea">
</div>
<div id="greatvideo">
</div>
</body>
There are two bugs in your code that are preventing this from working. Once you make these changes, you'll see the following output above your YouTube video:
5
3
1
0
The bugs are as follows:
Use quotes around callback, or use an anonymous function:
// put callback in quotes or anonymous function
myplayer.addEventListener("onStateChange", "onPlayerStateChange");
Make sure parameter matches local variable passed to log function:
function onPlayerStateChange(newState){
log(newState); // variables are case sensitive
}
NOTE: addEventListener may not work in IE browsers. I'm running Ubuntu and don't have IE available, but you may need to use browser detection to use attachEvent in IE browsers. This was tested on Chrome 8.0.
I also signed up for a jsapi key. The documentation was unclear as one google page said the API Key was required to use Google Loader, while the API page itself said it was not required. You may not need to obtain a key, but if you still have trouble after making the above changes, I would suggest you obtain a key to rule out this variable in the equation.
http://code.google.com/apis/loader/signup.html
Resources:
YouTube Adding Event Listeners
YouTube EventListener Player Demo
UPDATE: This works in Chrome 8.0, Firefox 3.6, and IE8. It does not work in IE7. But instead of wasting energy on a dead browser, do the community a favor and redirect those small subset of users to an upgrade link so they can join the year 2011. :) The IE7 build I used is part of IECollections and is not the real IE7.
If you have a subset of users who, for whatever reason, are tied to older versions of IE7, and you wish to support these users, please see javascript addEventListener onStateChange not working in IE