Wait for jPlayer to finish loading files before continuing loop - javascript

I'm using the jQuery audio player 'jPlayer', I have a loop which creates a number of jPlayer unique instances then assigns an on click 'jPlayer play' to another part of the document for each instance.. The problem is jPlayer uses an internal ready: function() {} to assign the audio path and load the file.. I need to wait for this function to finish before continuing the loop.
The pseudocode is something like this:
for loop{
create jPlayer div;
instantiate jPlayer and assign audio file in the ready(); // need to wait before continuing
assign an on click event to play file;
}
I'm convinced it's about using queues but I wouldn't know how to implement it? Any help would be most appreciated!

You might try something like this:
function initPlayer(playerQueue){
if(playerQueue.length > 0) {
var player = playerQueue.pop(); // get our options hash for this iteration
var container = $('<div id="'+player.container+'"></div>').appendTo('body');
$(container).jPlayer({
ready: function(){
var _e = this.element;
_e.jPlayer('setFile', player.file);
var timer = setInterval(function(){
if(_e.jPlayer('getData', 'diag.loadPercent') == 100){
$(player.control).click(function(){
// assign your handler
});
clearInterval(timer); // clear the interval
initPlayer(playerQueue); // invoke the next player init
}
},500);
},
/* ... other options ... */
});
}
}
initPlayer([
{container: 'audio-1', file: 'uri/for/file', control: '#button-1'},
{container: 'audio-2', file: 'uri/for/file', control: '#button-2'},
{container: 'audio-3', file: 'uri/for/file', control: '#button-3'},
{container: 'audio-4', file: 'uri/for/file', control: '#button-4'}
]);
Basically we get rid of the explicit loop and instead use the ready function itself to advance the build iteration via the initPlayer function. By usin an array we can use pop() which will get us that last element of the array while also removing it from the array. We wait to invoke initPlayer until after we get a load percent of 100 by polling the load percent of the player every 500 ms.
This is only a suggestion and may need a lot of work... ive never used jPlayer and am flying blind from the documentation so dont expect it to work out of the box :-).

Related

Javascript function not fired on video timeupdate

Currently working on a page containing a video that has to be paused at certain points (like chapters). So I made a function that will stop the video when it hits the next "time marker" which looks like this:
function vidPause(nextMarker){
var timeMarker = nextMarker;
if(videoPlayer.currentTime >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', vidPause());
}
};
And I'm trying to fire it this way:
videoPlayer.addEventListener('timeupdate', vidPause(nextMarker));
But it only seems to fire when the video is loaded. Nothing happens when the video is playing (tested by using a console.log(videoPlayer.currentTime); inside the vidPause function).
Note: I need the function to be called that way so that I can remove the event listener when it hits the time marker, that way it won't stop when the user wants to play the video from that point on.
Thanks in advance for any suggestions!
The function is being called once in the addEventListener line, but that's not actually passing it as a callback.
Try this:
function videoUpdate(e) {
vidPause(nextMarker, videoPlayer.currentTime;); // Now call your function
}
function vidPause(nextMarker, timeStamp){
var timeMarker = nextMarker;
if (timeStamp >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', videoUpdate);
}
};
videoPlayer.addEventListener('timeupdate', videoUpdate); // Note no brackets, as it's passing a ref to the function rather than calling it
I don't know what the scope of nextMarker is, but you should be able to start console logging and find out.

Two methods of looping through Ajax requests for jQuery Then When - which to use?

I've got a deceptively simple blog project in the works, where I'm trying to bring together Isotope Jquery (for layout/filtering/sorting), Infinite Scroll, and dynamic loading of all blog excerpts via Ajax (so filtering and sorting is applied to all excerpts before the user scrolls down the page (after which time they're loaded into the dom and then accessible)).
This question primarily deals with getting the blog post excerpt data via Ajax, to then be passed into Isotope filtering code. I'm not sure of the best way to do this, but am currently trying to loop through each page (of blog posts excerpts) with an ajax request and then access the data as one whole.
I've come across two different methods to loop through the ajax requests, each using then when jquery statements. The first is using the method give in this SO answer, the other is simply putting the entire then when statement inside of an $.each statement.
Method 1:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
var deferreds = [];
$(pageCountArray).each(function() {
var pageNumber = this;
deferreds.push(
$.get('/page/' + pageNumber)
)
$.when.apply($, deferreds)
.then(function(data){
console.log(data);
// this outputs data as a string from the first page, then a list of objects
console.log(typeof(data));
// string
// 13 - object
});
});
Slight aside: Any ideas as to why this is outputting one string and then objects?
Method 2:
var pageCount = 15;
var pageCountArray = [];
for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);
$(pageCountArray).each(function(data) {
var pageNumber = this;
$.when(
$.get('/page/' + pageNumber)
).then(function() {
console.log(data);
// this outputs 14 strings of data
console.log(typeof(data));
// 14 - string
})
});
I haven't yet figured out how to incorporate the Ajaxed data into my Isotope filter function, but I think I'll need to parse this into HTML first. Still getting my footing with javascript... in this case is one of these data types (objects vs strings) easier to parse into HTML? I suppose that's the key to my answer?
Much obliged for insights.
PS: Bonus points for anyone who might know of a better way to achieve this in a different way that somehow dovetails into Isotope/Infinite Scroll nicely (perhaps in a way that's more intended to play nice with these plugins... I've been unsuccessful in my searching).
PPS: The second method feels much cleaner... anyone know of a reason that it's not a good approach (using when then inside of an .each loop)?
Wow, this is a largely scoped question no wonder there aren't any responses. This is a massive question so I will do my very best to help. I have created many sites that include the sort/filtering of Isotope while using AJAX preload's with infinite scrolling so here is one of the simplest examples I have already written out...
First I must mention that this whole thing works much better with David DeSandro's ImagesLoaded plugin. This is mostly because it allows you to place a callback function (function to be executed once an event occurs) attached to the loading event of the final image in a given container. Wow that was wordy. How to put that better... It basically asks the container, are you done loading yet? No? How about now? You're loaded? Ok please do this function now then...
With that being implemented I would start with this code in my onLoad event like so...
$(function() {
extendJQ_PreLoad(); //I Will Get To This Function In A Min
//Use ImagesLoaded Plugin To Control Load Time Sync
$(container).imagesLoaded(function() {
cont.isotope({
itemSelector: ".box", //This is the class I use on all my images to sort
layoutMode: "masonry",
isOriginLeft: true,
isFitWidth: true,
filter: "*",
masonry: {
columnWidth: ".box"
}
});
preLoadNextImgSet(); //I Will Get To This Function In A Min
});
});
Ok so let's break this down. The ImagesLoaded plugin stops the Isotope plugin instantiation from happening before there are images present to sort/filter/load and/or handle. This is step 1. Step 2 would be to then start looking at the actual isotope plugin instantiation. I am telling it to use Masonry plugin as its layout style and then I am passing in an object literal with options under the array key 'masonry'. The array key here that is named masonry is the same as any instantiation you would have normally done in the past with the stand alone Masonry plugin (non-isotope or isotope-2).
Step 3 to look at here would be my beginning call to extendJQ_PreLoad();. This function is the function I wrote to let JQuery know that I need to extend it's core functionality in order to capacitate preloading any images I give it, as an array. Like so...
function extendJQ_PreLoad() {
$.preloadImages = function(args) {
for (var i = 0; i < args.length; i++) {
$("<img />").attr("src", args[i]);
}
}
} //end function
This is just a simple iterator and nothing fancy, it allows the images to be preloaded by using a neat trick associated with the DOM. If you load images in this way it loads then into memory but not into the DOM meaning it is loaded and hidden. Once you then insert this image anywhere it will insert very quickly as it is now loaded in cache and awaiting placement. You can view more about this here.
Finally the last to look at is my call to my preload function. This is a very simple call to a php file that simply goes and looks for the next set of images in order, if there is any to find. If it gets some images then it begins adding it to a temporary div in memory (again not on the DOM to be seen) and is now setup for simple DOM traversal. Let's view the function to dissect its functionality...
function preLoadNextImgSet() {
$.post('AjaxController/ajaxPreload_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
var y = $(document.createElement("div")).append(data).find("a"),
found = [];
y.each(function() {
found[found.length] = "img/gallery/" + $(this).text();
});
$.preloadImages(found);
}
});
} //end function
In this example I have two global variables living in my browser window from JavaScript that I would have declared. A start and a loadSize variable. The start variable represents the current place in our list of images that we currently are at and the loadSize variable sets a limit on how many images to preload each time.
Now that the variables are set and sent in to the PHP file via the $.post function, we can use the PHP file to find the appropriate images in order and have them loaded into memory awaiting usage. Whatever is returned here to the y variable gets iterated over by the each function and then preloaded. Once this functions scope is exited the imaginary div will be deleted and sent to garbage as it is not used simple iterated over.
Ok, now. Its been a journey but we are almost ready to begin the final method here. Let's first go back and look at what the first imagesLoaded call was doing now that we know the new functionality added in these functions. The imagesLoaded call in the DOM-Ready event has a call in its very bottom piece that preloads the images.... why? This is because once the page loads and the initial images are loaded into the isotope container, we need the page to now use this idle time to begin already loading the next set. So in other words once the images are placed and sorted and happy to just sit there, the next loadSize amount of images will be loaded and waiting for you to place them.
Now for the final function. This function is a generic function thats sole purpose is to load in the current preloaded images into the DOM officially and then to ask for the next set to be loaded. However what on earth would be calling this function? This is where the lazyloading or infinitescroll becomes useful to us. Somewhere in your page you need to add this function in...
$(window).scroll(function(){
scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
docuHeight = $(document).height();
//AJAX Data Pull
if(((scrollTop + windowHeight)+35) >= docuHeight){
getNextImages();
}
});
This function is the magic function that allows the infinitescroll effect to occur. I have added 35 pixels or so of padding (the +35 randomly in my code) because sometimes you want it to load close to the end of the page but not quite the actual end of the page.
Ok so now that this is setup when we reach the end of the page this function will want to get all of the next images generically like we had mentioned. The function of mine looks like this...
function getNextImages() {
cont = $(container);
$.post('AjaxController/ajaxPortfolio_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
if(data!="") {
//Append New Photos Inside <a> Element Tag
var y = $(document.createElement("div")).append(data).find("a");
cont.append(y);
//Fix Image Layouts
cont.imagesLoaded(function() {
//Feed Isotope Layout The New Items
cont.isotope("appended", y);
cont.find("a").css({"opacity":"1"});
});
} else { unFilled = false; }
});
}
I have included the unFilled variable simply so that there is a flag that can be set when you have reached the end of the images. You don't want it to keep trying to load forever if there are no images left to show.
Ok, so. This is a lot of information so I will try to keep answering as much as possible.

Use of reference to calling object (this) using HTML5 audio and jQuery

I'm creating my own HTML5 audio player capable of handling playlists. I have created a custom myPlaylist object with includes all play(), pause(), stop() and other needed functionality. This is all working correctly, but moreover, I need to be aware about when an audio file has ended in order to automatically start playing the next one.
Here's the relevant parts of the code I'm using:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
// Here I want to call to my next() function
});
}
I haven't been able to figure out how to do it. I've tried already several combinations, like $(this).next(), which seems the most reasonable and actually displays the alert, but then does nothing ¿?, also this.next(), which also displays the alert but then shows an error since this refers to the HTML5 audio element, which does not have a next() function.
I've also tried another approach, using
audio.onended = function(){
alert("Song is finished!");
$(this).next();
};
But those do not even trigger the alert. Also audio.ended does not work.
So, I'm basically clueless right now, does anyone have any idea what am I doing wrong? Thanks in advance.
Oh, and I've tested all this in the latest versions of Google Chrome and Safari in Mac OS X.
EDIT Following the advice given in HTML5 audio playlist - how to play a second audio file after the first has ended?, I've also tried the following code
player.addEventListener("ended", function() {
alert("Song is finished!");
$(this).next();
});
And
player.addEventListener("ended", next);
None of them work either, although the first one shows the alert properly.
EDIT 2 Using the search I came across this question, which might also have something to do with my problem, so in order to get rid of any possible troubles with the reference to this, I added a new variable referring to the object itself, so now I'm basically working with:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
var me = $(this);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
me.next();
});
}
But then I get an error saying that the Object does not have a method next().
I don't know what else can I try... Any extra information will be highly appreciated, thank you!
there's an HTML5 playlist example handling the ended event here, if that helps?
in your event handler you reference this, but in this context this refers to the DOM element that caught the event, i.e. your audio element.. try this instead:
function myPlaylist(){
var self = this;
var player = document.createElement('audio');
this.next = function (){
// Picks next song in the playlist and plays it
...
};
player.addEventListener("ended", function() {
alert("Song is finished!");
self.next();
});
}
see the MDN for more info on the this keyword

Sequencing Events in Javascript

I am trying to make a simple hidden object game using javascript. When the user finds and clicks an image, I want 3 things to happen in the following order; a sound plays, the image size increases, and the image goes invisible. The problem I am running into is getting the 3 events to happen sequentially, not concurrent. Right now, seems that all three events happen all at the same time.
I've tried using setTimeout(), and while that does create a delay, it still runs all functions at the same time, even if each function is nested in setTimeout.
Example: (all this does is waits 1.5 sec then plays the sound and makes the image invisible):
function FindIt(image, id){
var t = setTimeout('sound()',10);
var b = setTimeout('bigger(' + image + ')',30);
var h = setTimeout('hide(' + image + ')',1500);
}
Below are the functions I am currently using and the actual results are: click the image, nothing happens for 2 seconds, then the sound plays and the image goes invisible.
function FindIt(image, id){
sound();
bigger(image);
hide(image);
}
function sound(){
document.getElementById("sound_element").innerHTML= "<embed src='chime.wav' hidden=true autostart=true loop=false>";
}
function bigger(image){
var img = document.getElementById(image);
img.style.width = 112;
img.style.height = 112;
}
function hide(id){
var ms = 2000;
ms += new Date().getTime();
while (new Date() < ms){} //Create a 2 second delay
var img = document.getElementById(id);
img.style.visibility='hidden';
}
Any guidance would be greatly appreciated!
To trigger things sequentially, you need to execute the second item some amount of time after the first one completes, execute the third item some amount of time after the second one completes, etc...
Only your sound() function actually takes some time, so I'd suggest the following:
function FindIt(image, id){
sound();
// set timer to start next action a certain time after the sound starts
setTimeout(function() {
bigger(image);
// set timer to start next action a certain time after making the image bigger
setTimeout (function() {
hide(image);
}, 1000); // set this time for how long you want to wait after bigger, before hide
}, 1000); // set the time here for how long you want to wait after starting the sound before making it bigger
}
FYI, the animation capabilities in libraries like jQuery or YUI make this sort of thing a lot easier.
Also, please don't use this kind of construct in your JS:
while (new Date() < ms){}
That locks up the browser for that delay and is very unfriendly to the viewer. Use setTimeout to create a delay.
For reference, using the animation libraries in jQuery, the jQuery code to handle a click on the object and then animate it over a 2 second period to a larger size, delay for 1 second, then slideup to disappear is as follows:
$("#rect").click(function() {
$(this).animate({height: 200, width: 400}, 2000).delay(1000).slideUp();
});
jQuery manages an animation queue and handles setting all the timers and doing all the sequencing and animation for you. It's a lot, lot easier to program and gives a very nice result.
You can see it work and play with it here: http://jsfiddle.net/kC4Mz/.
why don't use "event" approach. like onTaskDone();
function task1(arg, onTask1Done){
console.log(arg);
if(onTask1Done)onTask1Done();
}
task1("working", function(){console.log("task2");});
The Frame.js library is designed to elegantly handle situations like this:
function FindIt(image, id){
Frame(10, function(next) { sound(); next(); });
Frame(30, function(next) { bigger(image); next(); });
Frame(1500, function(next) { hide(image); next(); });
Frame.start();
}
Frame.js offers many advantages over using standard timeouts, especially if you are doing a lot of this kind of thing, which for a game, you likely are.
https://github.com/bishopZ/Frame.js

How do I set up sequences of functions that I want executed in JavaScript?

I'm working on a JavaScript driven site where I will have a lot of stuff that need's to be executed in a certain order. A lot of the stuff involves animations and AJAX-loading. Some pseudo code could look like this:
Load JSON formated data
Generate HTML-elements using the loaded JSON data and render them inside a div
Make the elements inside the div scrollable using a jQuery UI slider
Randomize a number between 1 and the total number of loaded elements
Make the jQuery UI slider scroll (animate) to the element that represents the randomized number for a duration of 500 milliseconds
Load more JSON formated data
Replace other elements on the page
And so on...
Each step in this is wrapped in a function - one function loads the JSON data, another generates the HTML-elements, a third initializes the jQuery UI slider and so on. Encapsulating the code in functions makes the code easier to read for me, but above all I want to be able to call the functions in different orders depending on what happens on the page and I want to be sure that one function has finished running before the next one is executed.
If there was just regular functions that didn't involve AJAX or jQuery animations I'd just execute the functions I want to execute, one after the other. The problem is that I need to wait for the animations and data retrieving functions to finish before moving on. To aid me both the animation and AJAX methods in jQuery allow me to send along a callback. But here's where I get lost.
What I want it to do is the following:
Load JSON data. If the loading is successful, go on and...
Generate HTML-elements
Make the elements scrollble
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
Load more JSON formated data. If the loading is successful, go on and...
Replace other elements on the page
The ideal thing would be if I could set up this sequence/chain of events in one single place, for example inside an event handler. If I want to call the functions in a different order or not call all of them I would just set up a different sequence/chain. An example could be:
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
This means that I'd have to be in control over the callbacks in each step.
I hope you understand what I'm looking for. I want to control the entire execution sequence from a single function. This function would be "the conductor of the orchestra" and all the other functions would be the different instrument sections of the orchestra. This conductor functions need's ears so it can hear when the violinist is finished with her solo and can tell the horns to start playing. Excuse me for the corny allegory, but I hopes it makes it easier to understand what I want to do.
Thanks in advance!
/Thomas
Would the jQuery .queue() function help you?
Could you store a sequencer variable that is an array (which you would be able to change) and then call a sequencer at the end of each function?
You could then pass a step code through each function and cross-reference that with the sequencer variable as to what the next step should be.
Pseudo Code:
var func['1'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['2'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['3'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['4'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
function sequencer(seq) {
seq = seq + 1;
window.func[seq]
}
I tried executing this code:
var seq = 0;
var func = [];
func[1] = function(seq) {
setTimeout(function() {
console.log("Executing function 1");
sequencer(seq);
}, 2000);
}
func[2] = function(seq) {
console.log("Executing function 2");
sequencer(seq);
}
func[3] = function(seq) {
console.log("Executing function 3");
}
function sequencer(seq) {
seq = seq + 1;
func[seq].call();
}
sequencer(seq);
But the result (in Firebug) is:
Executing function 1
func[seq] is undefined
[Break on this error] func[seq].call();
I think that the problem is caused by context, but I'm not sure. JavaScript is sensitive to the context in which a function is called.
/Thomas
I found that what I was trying to achieve was slightly overkill for my purposes. So I decided to go with a different approach. I can send one or more boolean variables as a parameters to a function and use them to decide whether to execute a second function or not. Here's an example:
$("#justOneStep").click(function() {
loadImage(false);
});
$("#twoStepsPlease").click(function() {
loadImage(true);
});
function loadImage(boolDoMore) {
// Do the stuff that loads an image
...
if(boolDoMore) {
nextFunction();
}
}
function nextFunction() {
// Do some more stuff
...
}
Not very fancy but easy to understand and control and sufficient for my needs at the moment.
/Thomas

Categories

Resources