I'm working on making an object to wrap controls around instances of html5 audio elements. For testing, I've made an object like this (simplified for readability)..
function AudioObject(audio) {
var innerAudio = audio;
this.Play = function () {
innerAudio.play()
}
}
And I have an array holding instances of this AudioObject.
var AudioObjects = [];
Then, when creating new AudioObjects, I add them to the array. I have a function that plays the AudioObjects with delays so they play in sequence. Something like this:
var audioObj = new AudioObject(audio);
AudioObjects.push(audioObj);
....
....
var delay = 0;
$.each(AudioObjects, function(iterator, obj) {
setTimeout(obj.Play, delay);
delay = delay + 3000;
}
When there is just one audio file, it plays correctly, but once I add more AudioObjects, each one in the array has their innerAudio variable set to the latest created Audio element. I can play each one individually from the html. I've debugged on creation and I can see it's setting the right audio, but after being pushed to the array, the objects in the array all get switched to the latest innerAudio.
Am I not instantiating my objects correctly? I've made this jsfiddle to experiment with alerting text, and it doesn't seem to have the issues I'm experiencing.
Update: I think I found my issue. This is how I thought you write a "public" and a "private" function inside of an object.
function Thing(test) {
var thingTest = test;
// Public function
this.PublicTest = function () {
setTimeout(Test, 1000);
}
// Private function
PrivateTest = function () {
alert(thingTest);
}
}
This is essentially how my code was structured, but the "private" function has a different scope than the public one. I double-checked my array, and it was actually saving the right audioObject with the correct audio source, but when running the function that calls the "private" function, it calls the wrong audio file.
I've updated the jsfiddle to show what's happening. I thought it would alert "test1" then "test2" but it alerts "test2" twice.
Update 2: For the record, changing the private function to this fixed the problem:
function Play() { // <<< That's all I had to change!
innerAudio.play()
}
Do you create unique aduio object for each audio wrapper?
var audio = new Audio();
audio.src = 'src';
var audioObj = new AudioObject(audio);
AudioObjects.push(audioObj);
Related
I ran into an issue in my current project where I needed to access the array that held an object, as the event listener that an object inside that object needed to access all of the elements of the array. So, I did a little test and discovered for myself that you can, in fact, store the container array that contains an object inside that object and it will actually reference the original array, not a copy.
There are some fairly obvious problems with this in terms of being maintainable code, but I'm curious as to what the potential harm of doing this is. Is there anything I should know regarding this recursive property scenario before I put this in my project? Is there a better way to do what I'm looking to do?
For clarity: the way I'm planning on setting it up is as follows:
The array (linesArray) contains a series of objects of class GraphLine. Each Graphline object (lineObject) contains a Raphael canvas element object (line) for the purposes of formatting that element post drawing. It would also contain a reference property to linesArray (container)
I am planning on having the lineObject.line.mousedown() [which fires on clicking the line] event run a for loop through lineObject.container, aka linesArray, to transform each of the lineObject.line's within it based off of which lineObject.line fired the lineObject.line.mousedown() event.
It would look something like this:
class GraphLine {
id;
line;
lineStr;
container;
constructor(container, /* Bunch of Inputs */) {
this.container = container;
//...
}
Draw() {
this.line = canvas.path(this.lineStr);
this.line.mousedown( function() {
for(let i = 0; i < this.container.length;i i++) {
if(this.container[i].id != this.id) {
//Do A Thing
}
else {
//Do A Different Thing
}
}
});
}
}
var canvas;
$(document).ready(function() {
function Run() {
var container = [];
canvas = new Raphael($('#canvas'), 100, 100);
container.push(new GraphLine(container, /* Bunch of Inputs */));
//...
container[0].Draw();
}
Run();
});
I've built a presentation player that creates its slides as iframes and I'm trying to clean up my code now that I have the prototype working. I'm having issues finding the best way to handle the interaction between the slides and the presentation player, though. The slides need to do things like tell the player that they have loaded or that they have finished playing.
I made a simplified example of the interaction the player and slide instances need to have...
function Parent() {
this.child = undefined;
this.makeChild = function() {
this.child = new Child(this);
};
this.payAttention = function(message) {
console.log('Child says, "' + message + '"');
};
}
function Child(parent) {
this.getAttention = function() {
parent.payAttention('I\'m hungry.');
};
}
var dad = new Parent();
dad.makeChild();
dad.child.getAttention();
This pattern is working, but I'm wondering if this is the most efficient way to handle this. Is there a better way to do this, or is this okay?
You could save a few lines with inserting an object to the variable:
this.child={};
this.makeChild=function(){
a=function(){
this.payAtention("Im Hungry");
}
a.bind(this);
this.child.getAttention=a;
}
If you never dealt with .bind(scope):
Bind binds the object passed as the parameter to the this inside the function.
By the way, if there are multiple childs:
this.child=[];
this.child.push({name:"Tim"});
I want to instantiate a new MediaElementPlayer object. When it's successfully created, I want to pass the whole object on to another function (my_object.attachEvents). My code is as follows:
var options = {
success: function () {
//point 2
console.log("passing player object", local_player_instance);
my_main_object.attachEvents(local_player_instance);
}
}
//point 1
console.log('player inited', local_player_instance);
local_player_instance.video = new MediaElementPlayer('#video_player', options);
my_main_object.attachEvents = function(local_player_instance) {
local_player_instance.video.play()
}
In Firefox, the assignment at point one is executed before the line at point 2 calls the attach events method.
Im Chrome, point 2 is evaluate first, and as a result when the play method in the attach events function is called it doesn't exist.
My question is, how do I pass successfully pass the MediaElementPlayer to another function when it is created?
The best way to handle this in a cross browser way is
// here's where you'll store a global reference to the player
var globalMediaElement = null;
var options = {
success: function (domNode, mediaElement) {
globalMediaElement = mediaElement;
doStuff();
// you can also get the the player via jQuery here
$('#video_player').player
}
}
// create MediaElement
new MediaElementPlayer('#video_player', options);
function doStuff() {
globalMediaElement.play();
}
I am making flash player that suppose to be controlled from outside, from javascript.
I need those methods:
Play/Pause and Volume level
I am stuck with volume level... I tried to add this code:
flashMovie.volume = 10;
Where flashMovie is flash instance... And it's show NO ERROR but it's NOT WORKING
I try to make inner AddCall(); and then when it's called to call() from javascript to return sound level.
AS 3:
function setthisvolume()
{
var vlm = ExternalInterface.call('giveMeVolume()');
this.soundTransform.volume = vlm;
}
ExternalInterface.addCallback("setthisvolume", setthisvolume);
JS:
var soundlevel = 10;
function soundlevelset()
{
var flashMovie=getFlashMovieObject("objswf");
flashMovie.setthisvolume();
}
function giveMeVolume()
{
return parseInt(soundlevel);
}
But I am getting this error:
Error calling method on NPObject!
I even tried with setInterval():
AS 3:
function setthisvolume()
{
var vlm = ExternalInterface.call('giveMeVolume()');
this.soundTransform.volume = vlm;
}
setInterval(setthisvolume, 1000);
JS:
var soundlevel = 10;
function giveMeVolume()
{
return parseInt(soundlevel);
}
And it doesn't show any error, but it doesn't work neither...
Did someone work with stuffs like this?
Can someone help me what I am doing wrong here...
Thank you!
Thank you, #someone!
This second option worked okay!
Here is working code:
AS3:
function setthisvolume(vlm)
{
this.soundTransform = new SoundTransform(vlm);
}
ExternalInterface.addCallback("setthisvolume", setthisvolume);
JS:
function getFlashMovieObject(movieName)
{
if (window.document[movieName])
{
return window.document[movieName];
}
if (navigator.appName.indexOf("Microsoft Internet")==-1)
{
if (document.embeds && document.embeds[movieName])
return document.embeds[movieName];
}
else
{
return document.getElementById(movieName);
}
}
var soundlevel = 0.5; // it's 0-1 volume, not 0-100
function soundlevelset()
{
var flashMovie=getFlashMovieObject("objswf");
flashMovie.setthisvolume(parseFloat(soundlevel));
}
When you are using slider each time slider change you need to change soundlevel variable and call soundlevelset();
Hope I helped next one who is starting with this... :)
Thank you!
Try removing the parentheses when calling giveMeVolume, by changing this:
var vlm = ExternalInterface.call('giveMeVolume()');
to this:
var vlm = ExternalInterface.call('giveMeVolume');
If that doesn't work, try passing the volume directly as an argument/parameter, like this (this is probably a better way to do it):
AS3:
function setthisvolume(vlm)
{
this.soundTransform.volume = vlm;
}
ExternalInterface.addCallback("setthisvolume", setthisvolume);
JS:
var soundlevel = 10;
function soundlevelset()
{
var flashMovie=getFlashMovieObject("objswf");
flashMovie.setthisvolume(soundlevel);
}
Code looks reasonable.
Check if you allow Flash to communicate with script There is property when you create Flash object - AllowsScriptAccess - http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c9b.html .
Check if Falsh is coming from the same domain as HTML page.
For addCallback check if you are getting correct Flash object by Id (the way to create Flash is different in IE/FF, so you may be getting the wrong one).
Check if you have correct SWF file - browser may cache older version... I.e. add element on the Flash control that simply shows static number and make sure it matches to latest one.
I'm trying to create a robust audio player in javascript (& jQuery). I know that there are other players out there, but I'd like to try creating my own (so please don't refer me to jquery plugins). This is essentially what I would like to do:
Main.js:
var player = new Player(AudioObj); // Audio object links to Audio class (not shown)
player.buttons.play = $('play');
player.buttons.pause = $('pause'); // Play and pause ID's link to HTML Document Element
Player.js:
Player = function(Audio) {
this.Audio = Audio;
this.buttons = {};
for(var button in this.buttons) {
button.live('click', this.button); // This is the line I Have NO idea about..
}
}
Player.prototype = {
play : function() {
// Do Something
},
pause : function() {
// Do something
}
}
So essentially, I would like the properties to be pre-linked to object functions when you initialize the Player, and to just have it work when I link it to an HTML element.
Thanks!
Matt Mueller
I think this would be a more OO way to go. Setup two more functions inside Player. One function would register a UI element to a Player action and another to unregister the action. So rather than keeping an explicit button collection you can just lean on jQuery.live and jQuery.die. For example:
function registerAction(selector, action) {
// you could have some logic to map the passed in action
// to the actual function name
$(selector).live('click', action/functionName);
}
function unRegisterAction(selector, [action]) {
// you could have some logic to map the passed in action
// to the actual function name
$(selector).die('click', [action/functionName]);
}
Then, your main.js example from above would become:
var player = new Player(AudioObj); // Audio object links to Audio class (not shown)
player.registerAction('#play', play);
player.registerAction('#pause', pause); // Play and pause ID's link to HTML Document Element
And your Player constructor would become:
Player = function(Audio) {
this.Audio = Audio;
}
Or something like that.
This is not the perfect solution but I find it to be pretty elegant:
Player.js
Player.prototype = {
init: function() {
var Player = this;
// Attach buttons to respected functions
for(var button in this.buttons) {
if(typeof Player[button] === "function")
$(this.buttons[button]).bind('click', {Player : this}, Player[button]);
}
},
play: function(e){
var Player = e.data.Player;
var Audio = Player.Audio;
Audio.play();
},
pause: function(e){
var Player = e.data.Player;
var Audio = Player.Audio;
Audio.pause();
}
}
Main.js
var audio = new AudioCore("UpToYou.mp3");
var player = new Player(audio);
player.buttons.play = $('#play');
player.buttons.pause = $('#pause');
player.init();
This provides a nice way to link buttons to the function without passing in a huge array or providing a bunch of options. I would be VERY happy to have a solution that would NOT require you to call init().