calling actionscript 3 function from javascript with addCallback not working - javascript

I have a requirement in a project which has to call an actionscript 3 function from javascript. I read many answers from stackoverflow and adobe and have created a simple test application. In that, I have used ExternalInterface.addCallback to register both the functions and I've also included the security permissions in both actionscript and in html.
Action Script 3 Embedded code
I did not use an external .as file instead i used the action panel to create the code.
import fl.controls.Button
import flash.events.MouseEvent;
flash.system.Security.allowDomain("*")
var butt:Button = new Button();
addChild(butt);
butt.label = "hello";
butt.toggle = true;
butt.move(50, 50);
butt.height = 100;
butt.addEventListener(MouseEvent.CLICK, clickhandle);
function clickhandle(e:MouseEvent)
{
if(butt.height == 100)
butt.height = 200;
else butt.height = 100;
}
ExternalInterface.addCallback("callas", clickhandle);
The test app will display a ac3 button. Upon clicking that button it will toggle the expanding and collapsing of its height through a click handler 'clickhandle'. This is the function i am intended to call from javascript.
HTML Code
<html>
<head>
<script>
function callas() {
var hehe = document.getElementById('cvpanel');
console.log(hehe);
hehe.clickhandle();
}
</script>
</head>
<body>
<p>Test</p>
<object id='cvpanel' name='cvpanel' width="425" height="350" type="application/x-shockwave-flash" data="one.swf">
<param value="one.swf" name="movie">
<param name="allowscriptaccess" value="always"/>
<p>You need Flash to see it.</p>
</object>
</body>
</html>
The application runs under http://localhost through apache and all the files needed are in the same folder. I could run the application and could see the buttons appearing. By clicking it they toggle in height as expected.
When i call the function callas from browser console i get the error in console as
"TypeError: hehe.clickhandle is not a function"
in firefox and similar in Internet Explorer.
Is there anything missing?

In flash you're exposing clickhandle as callas
ExternalInterface.addCallback("callas", clickhandle);
so you're supposed to call callas() from javascript
var hehe = document.getElementById('cvpanel');
hehe.callas();

Related

ActionScript3 (AS3) External Interface call to javascript/jquery function not working

I am working on a project that is intended to track users using Local Shared Objects. The user first visits page A, which has an embedded .swf that plants a Local Shared Object. I know it works by testing it my own flash cookies.
Upon visiting page B, another embedded .swf will attempt to retrieve the flash cookie and call an AJAX function if the cookie is present. Page B is what I am having trouble with. On this webpage is embedded a .swf object, along with an AJAX function. The following code is the HTML for page B. It includes the AJAX script, along with an embedded .swf.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
function databaseAndemail(){
$.get('http://www.mywebsite.com/databaseAndemail.php');
}
</script>
</head>
<body>
<object width="1" height="1">
<param name="CheckLSO" value="http://www.mywebsite.com/CheckLSO.swf">
<embed src="http://www.mywebsite.com/CheckLSO.swf" width="1" height="1">
</embed>
</object>
</body>
The problem is not with the AJAX function, nor the PHP script, because I know that works. The problem may lie within the .swf script that is meant to check the LSO. This is the AS3 script called CheckLSO.swf:
public function Main():void
{
//Check for a LSO
var myLocalData:SharedObject = SharedObject.getLocal("myData");
if (myLocalData.data.uname != null){ //LSO exists, so call AJAX function to update database and send email.
if (ExternalInterface.available){
ExternalInterface.call("databaseAndemail");
}
};
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
I'll include the AS3 script that sets the cookies here for your information (again, I've tested this one and it works):
public function Main():void
{
// Create a new SharedObject
var myLocalData:SharedObject = SharedObject.getLocal("myData");
// Save data to the SharedObject
myLocalData.data.uname = "ERE";
myLocalData.flush();
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
To sum it up, I think for some reason the Javascript function is not being called by External Interface. I know the cookie is set, I know the AJAX works, and I know the PHP script works (I haven't included the PHP, it seems like it would be outside the scope of this question). Both .swf scripts are debugged. I use FlashDevelop with Flex 4. These are really simple scripts, so I can't think of anything else that might be going wrong. Any ideas??
Might be the script access setting. On the embed, try including the param allowscriptaccess: "always". External Interface calls will fail without it.
<object width="1" height="1">
<param name="CheckLSO" value="http://www.mywebsite.com/CheckLSO.swf">
<param name="allowscriptaccess" value="always">
<embed src="http://www.mywebsite.com/CheckLSO.swf" width="1" height="1"></embed>
</object>
Better yet, use swfObject 2 to embed your swf.
SwfObject

ActionScript Call Function from JavaScript

I'm trying to call a very simple JavaScript function from ActionScript but I can't seem to make it work. Here is my code:
JavaScript
function alert() {
alert("hi");
}
ActionScript
ExternalInterface.call("alert");
HTML
<object width="500" height="500"
classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://fpdownload.macromedia.com/
pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0">
<param name="SRC" value="FlashApp.swf"/>
<param name="allowScriptAccess" value="always" />
<embed src="FlashApp.swf" width="500" height="500" allowScriptAccess="always">
</embed>
</object>
Maybe it's because there is already an alert function? This works for me.
//js
function myAlert() {
alert("hi");
}
//as3
ExternalInterface.call("myAlert");
The AS3 code :
public class Main extends Sprite
{
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
ExternalInterface.call("myAlert");
}
}
Make sure that the javascript is initialized before you call that method. Depends on luck, really, unless you work at it. I suspect Baris Usakli has a fast pc and the js is initialized faster than the swf is.
Use swfobject to embed the swf when the javascript onload event occurs. This way you KNOW the js function exists and you can call it.
Bonus points, swf object embeds it for most if not all browsers, with no fuss and with negligible overhead.

Reading metadata from the <track> of an HTML5 <video> using Captionator

I am having trouble getting a working example that reads metadata from a WebVTT file, which was specified by the <track> element of a <video> in an HTML5 page. To be clear, I am not talking about reading the metadata out of the video file itself (as you would with an MPEG Transport Stream, for instance). What I'm talking about is the <track> element that is used for captioning videos. One of the attributes of a <track> is kind, which can be specified as any of the following values:
Subtitles
Descriptions
Captions
Navigation
Chapters
Metadata
I am trying to use the metadata type to access text stored in the corresponding WebVTT file, which I intend to manipulate using JavaScript. I know this is possible, as it is mentioned by Silvia Pfeiffer as well as by the maker of Captionator, which is the JavaScript polyfill that I am using to implement the functionality of interpreting the <track> tags. However, I just can't get it to work.
My code is based on the Captionator documentation's captions example. I added a button to retrieve the metadata and display it when I click the button. Unfortunately it keeps displaying "undefined" instead of the metadata. Any ideas what I might be doing incorrectly? Alternatively, does anyone know where a working example is that I could take a look at? I can't find one anywhere.
If you care to take a look at my code, I've included it below:
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Video Closed Captioning Example</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" media="screen" href="js/Captionator-v0.5-12/css/captions.css"/>
</head>
<body>
<h1>HTML5 Video Closed Captioning Example</h1>
<div>
<p id="metadataText">Metadata text should appear here</p>
<input type='button' onclick='changeText()' value='Click here to display the metadata text'/>
</div>
<video controls autobuffer id="videoTest" width="1010" height="464">
<source src="http://localhost:8080/Videos/testVideo.webm" type="video/webm" />
<source src="http://localhost:8080/Videos/testVideo.mp4" type="video/mp4" />
<!-- WebVTT Track Metadata Example -->
<track label="Metadata Track" kind="metadata" src="http://localhost:8080/Videos/Timed_Text_Tracks/testVideo_metadata.vtt" type="text/webvtt" srclang="en" />
</video>
<!-- Include Captionator -->
<script type="text/javascript" src="js/Captionator-v0.5-12/js/captionator.js"></script>
<!-- Example Usage -->
<script type="text/javascript" src="js/Captionator-v0.5-12/js/captionator-example-api.js"></script>
<script type="text/javascript">
window.addEventListener("load",function() {
captionator.captionify(null,null,{
debugMode: !!window.location.search.match(/debug/i),
sizeCuesByTextBoundingBox: !!window.location.search.match(/boundingBox/i),
enableHighResolution: !!window.location.search.match(/highres/i),
});
var videoObject = document.getElementsByTagName("video")[0];
videoObject.volume = 0;
document.body.appendChild(generateMediaControls(videoObject));
},false);
function changeText() {
document.getElementById('metadataText').innerHTML = testVar;
var cueText = document.getElementById("video").tracks[0].activeCues[0].getCueAsSource();
document.getElementById('metadataText').innerHTML = cueText;
}
</script>
</body>
</html>
My WebVTT file looks like this:
WEBVTT
0
00:00.000 --> 00:04.000
Testing 1 2 3 . . .
The way you're accessing the cue is correct - no problems there (although there will be a change in Captionator 0.6 from the .tracks property to the .textTracks property to be more in line with the specification. If you can bear the occasional bug I would recommend using 0.6 for its greater standards compliance - I've written the below code to use .textTracks - substitute for .tracks if you'd like to continue using the stable branch.)
The issue relates to the loading of the text tracks themselves. At the moment, you're not actually telling Captionator to load the track. Because this happens asynchronously, and on request, there is that inevitable delay where their content isn't available, you'll need to write your code in a way which accommodates for loading time and the potential load error.
You're also not waiting for Captionator itself to load - potentially a user could unknowingly click the button before this had occurred - triggering a nasty JavaScript error. This won't be such a problem when testing on your local box, but as soon as you deploy to the internet you'll be seeing all sorts of race conditions and other nasties. Consider disabling the button until both the page and the caption data have loaded.
I've tried to make the Captionator API as close as possible to the actual JS API which will be landing in browsers very soon - so in future this will be the same way you'll interact with the native browser functionality. As soon as the functionality is available natively, Captionator will bow out of the way, and your code should (assuming they don't change the API again!) just work with the native API.
First of all, you need to actually request that Captionator load the content. This is done my setting the 'display mode' of the track to SHOWING, or 2.
var video = document.getElementByID("myVideo");
video.textTracks[0].mode = 2; // SHOWING
Alternately, you can assign the status of a track to HIDDEN (1) - which still triggers a load, and cueChange events will still fire - but won't paint cues to screen. In Captionator, I don't paint metadata tracks to screen at all, but the (buggy) WebKit API in development will.
video.textTracks[0].mode = 1; // HIDDEN
Then you need to listen for when the cues are loaded and available:
video.textTracks[0].onload = function() { /* Your Code Here... */ }
Or when something goes wrong:
video.textTracks[0].onerror = function() { /* Whoah, something went wrong... */ }
Once the content is loaded, you can access the TextTrack.cues array (well, technically a TextTrackCueList.) Before the load has occurred, the TextTrack.cues property will be null.
var myCueText = video.textTracks[0].cues[0].text;
Be aware that Captionator parses the cue text of every cue, except when the track kind is metadata - so ensure you assign the correct track kind. You might end up with data or tags Captionator thinks are 'invalid' being thrown out. You can turn this check off for regular cues as well, with by setting the processCueHTML option to false.
With that in mind, here's how I'd rewrite your code:
<div>
<p id="metadataText">Metadata text should appear here</p>
<input type='button' onclick='changeText()' value='Click here to display the metadata text' id="changetext" disabled />
</div>
<video controls autobuffer id="videoTest" width="512" height="288">
<!-- Your video sources etc... -->
<!-- The metadata track -->
<track label="Metadata Track" kind="metadata" src="metadata.vtt" type="text/webvtt" srclang="en" />
</video>
<!-- Include Captionator -->
<script type="text/javascript" src="captionator.js"></script>
<script type="text/javascript">
document.addEventListener("readystatechange",function(event) {
if (document.readyState === "complete") {
captionator.captionify();
document.querySelectorAll("#changetext")[0].removeAttribute("disabled");
}
},false);
function changeText() {
// Get the metadataText paragraph
var textOutput = document.querySelectorAll("#metadataText")[0];
// Get the metadata text track
var metadataTrack = document.querySelectorAll("video")[0].textTracks[0];
if (metadataTrack.readyState === captionator.TextTrack.LOADED) {
// The cue is already ready to be displayed!
textOutput.innerHTML = metadataTrack.cues[0].text;
} else {
// We check to see whether we haven't already assigned the mode.
if (metadataTrack.mode !== captionator.TextTrack.SHOWING) {
textOutput.innerHTML = "Caption loading...";
// The file isn't loaded yet. Load it in!
metadataTrack.mode = captionator.TextTrack.SHOWING; // You can use captionator.TextTrack.HIDDEN too.
metadataTrack.onload = function() {
textOutput.innerHTML = metadataTrack.cues[0].text;
}
metadataTrack.onerror = function() {
textOutput.innerHTML = "Error loading caption!";
}
}
}
}
</script>
Here, we're disabling the button, preventing users on slow connections (or just somebody with very quick reflexes!) from hitting it before either Captionator or the metadata track are ready, and listening to a load event - at which point we re-enable the button, and can retrieve the cue text as normal.
You may need to load your metadata VTT file via Ajax and parse and display it yourself.
I looked at the example from the HTML5 Doctors' article on video subtitling. They're using Playr, so I checked out its source code, and they're definitely requesting the VTT file asynchronously and parsing the content once it's loaded.
I was able to load the contents of the VTT file and dump it into the specified element with the following code:
function changeText() {
var track = document.getElementById("videoTest").querySelector("track");
var req_track = new XMLHttpRequest();
req_track.open('GET', track.getAttribute("src"));
req_track.onreadystatechange = function(){
if(req_track.readyState == 4 && (req_track.status == 200 || req_track.status == 0)){
if(req_track.responseText != ''){
document.getElementById("metadataText").innerHTML = req_track.responseText;
}
}
}
req_track.send(null);
}
I'm not familiar with Captionator, but it looks like it has some capabilities for parsing VTT files into some sort of data structure, even if it doesn't necessarily support the metadata track type. Maybe you can use a combination of this code and Captionator's existing VTT parser?

Why does my call from flash to Javascript fail in IE9?

I have a couple of buttons in my flash application that call two different javascript functions. They work in all browsers except IE9 (I haven't tried earlier IEs). My code to call the function is something like this:
ExternalInterface.call(
"myLovelyFunction",
string1, string2);
and the code in the JS looks like this:
function myLovelyFunction(string1, string2) {
window.open('https://twitter.com/share?url=http%3A%2F%2Fwww.mysite.com%2Fapage.php&via=atwitteraccount&text=Some%20text%22&related=atwitteraccount',
'windowname',
'height=290,width=510');
}
In IE9, the function does absolutely nothing, but the console complains with:
SCRIPT438: Object doesn't support property or method 'SetReturnValue'
index.php, line 1 character 1
line 1, character 1 is obviously pointing at nothing in particular.
I can make it work fine by switching on compatability view, although the console error doesn't go away.
Is there anything about IE9 that causes this, and more importantly, how do I fix this?
I had this very same issue as well. I was using the following code:
<object type="application/x-shockwave-flash" data="/files/banners/64/64_300x250.swf" width="300" height="250">
<param name="movie" value="/files/banners/64/64_300x250.swf"/>
<param name="wmode" value="transparent"/>
</object>
I'm just embedding the flash with a regular object tag (no SWFObject and no embed fallback). My flash file calls the JS function window.open via ExternalInterface like this:
ExternalInterface.call("window.open", url, target, features);
What didn't work:
The link above suggests changing it to "document.open", which did not work. Also attempting to force the page to render in IE-8 mode did not work. For example:
<meta http-equiv="X-UA-Compatible" content="IE=8" />
What did work: Simply adding a "name" and "id" to the object tag solved the issue. For example:
<object type="application/x-shockwave-flash" data="/files/banners/64/64_300x250.swf" width="300" height="250" name="flash_object" id="flash_object">
Had absolutely the same issue, link below helped to solve it.
http://msdn.microsoft.com/en-us/library/gg622942%28v=VS.85%29.aspx

External Interface Problem: Error calling method on NPObject

Making a flash video streaming web-app using Actionscript 3's external Api. I am at a very rudimentary phase of the project where we're just experimenting with external interface. Right now i just have a flash object and 3 buttons each to play pause and load a video. The only button that is currently set up to do anything is the load button. My swf and my html file sit on the same file system, and my video files sit on another server with traffic being redirected through a media server.
When i press the load button, which should just give it the path of the video file on it's server. Instead it throws an error that reads "Error: Error Calling method on NPObject".
Without further adieu, here are snippets of relevant code:
ACTIONSCRIPT:
function loadVideo(newVideo){
clearInterval(progressInterval);
videoFile = newVideo;
stream.play(videoFile, 0, 0);
videoPositions = "0,0";
};
ExternalInterface.addCallback( "loadVideo", loadVideo );
JAVSCRIPT: (in the head of my html document)
<head>
<title> External API Test</title>
<script type="text/javascript">
var player = getFlashMovie();
if (typeof MY == 'undefined')
{
MY = {};
}
MY.load = function()
{
console.log('load called');
getFlashMovie().loadVideo("/media/preview/09/04/38833_2_720X405.mp4");
};
function getFlashMovie()
{
var isIE = navigator.appName.indexOf('Microsoft') != -1;
return (isIE) ? window['MYVID'] : document['MYVID'];
}
</script>
</head>
HTML:(in same document as javascript)
<body>
<div> This is a test</div>
<div class='my-media-player'>
<object width="720" height="405" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,16,0" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id='MYVID'>
<param value="as3VideoPlayer.swf" name="movie">
<param value="high" name="quality">
<param value="true" name="play">
<param value="false" name="LOOP">
<param value="transparent" name="wmode">
<param value="always" name="allowScriptAccess">
<embed width="720" height="405" name='MYVID' allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" loop="false" play="true" src="as3VideoPlayer.swf" wmode="transparent">
</object>
<div>
<button type="button" class='play'>Play</button>
<button type='button' class='pause'>Pause</button>
<button type='button' class='load' onclick='MY.load()'>Load</button>
</div>
</div>
</body>
Where is my mistake? I've read in a lot of places that this is an issue with security, but both my swf and html are in the same folder on my local machine. Only the files come from outside, and in any case I think i've set the security settings correctly when i declare my flash player in the object tag, but maybe i'm missing something there.
if you can't solve my question directly can someone please explain what "error calling method on NPObject" means? I'm sure its specific to flash-js communications because i've never seen it before and that's what i have gathered from my googling.
Thank in advanced.
I highly suggest SWFObject. Aside from that, I dare say you need to allow script access:
<script type="text/javascript">
// put your needed vars in the object below
var flashVars = {};
var params = {
allowScriptAccess:"always"
};
swfobject.embedSWF("myswf.swf", "myswf", "500", "400", "10.0.0", "", flashVars, params);
</script>
Can you try setting Security.allowDomain('*'); in your AS3 code right when it starts up?

Categories