setInterval and displaying objects in an array against a timeline - javascript

I need help creating an efficient JS listener against an array of objects and a video. It works, but is really inefficient and memory intensive, especially if the array of objects is large.
I have a video that is 1 minute long and I want to display objects that are synchronized with this video on the GUI based on timecodes. For example, at 0:23, I want to display a comment.
The synchronized, timecoded objects are stored in an array like this:
comm1 = {text : "I like it", time : 23}
comm2 = {text : "I hate it", time : 48}
comm3 = {text : "I love it", time : 59}
var arr = [comm1, comm2, comm3]
I then have a Flash video player that returns the current playhead position of the video. I can call it through JavaScript like so:
var position = document.getElementById("player").currentTime();
I then created a setInterval object that loops through the array every 250 ms to display the comment like so:
function getCurrentComment(){
for(i=0;i<arr.length;i++){
if(position >= arr[i].time){
getId("comments").innerHTML = arr[i].text;
}
}
}
var commListener = setInterval("getCurrentComment()", 250);
This works visually, but in reality, it continues to execute the "getId("comments").innerHTML = arr[i].text" line of code every 250 ms no matter what. Is there better way to do this without having to continuously loop through an array? It gets really bad when there are 100+ objects in the array. I was looking at the Observer Pattern but I don't think it will work for this since I'm synchronizing in real time with a video.

If you can listen to the seek event, then you can do something like this:
var arr = [
{text : "I like it", time : 23},
{text : "I hate it", time : 48},
{text : "I love it", time : 59}
],
ceil = arr.length - 1,
current = 0,
el = getId("comments"),
commListener;
function commListenerFunc()
{
commListener = setInterval(function ()
{
if (arr[current].time == position)
{
el.innerHTML = arr[current].text;
if ( current < ceil )
{
current++;
}
else
{
clearInterval( commListener );
}
}
}, 250);
}
// Call this function on the seek event
function seekCallback()
{
clearInterval( commListener );
current = 0;
for ( var i = 0; i < ceil; i++)
{
if ( arr[i + 1].time < position )
{
current++;
}
else
{
break;
}
}
commListenerFunc();
}
commListenerFunc();
Please note: I did not debug this code, and it might have some issues. I'm just posting it here to give you the general idea...

If you sort your arr and set var nextComm = 0, you can just test against the arr[nextComm]. If that one gets displayed, increment nextComm. If there's a seek, find the appropriate nextComm by seeking through arr for the closest future match.
Also, don't set innerHtml when you don't have to.

Related

Appending Different Random Number To URL In Javascript Array On Each Loop

I'm trying (without much success) to create an array which contains slides being loaded into an iframe. One of these frames (/Events.php) uses PHP to query a WordPress database and show 1 post chosen at random. This slide needs to show a different random post every time the array loops through.
My code at them moment is...
<script type="text/javascript">
var frames = Array(
'http://www.example.com/Slide01.php', 5,
'http://www.example.com/Slide02.php', 5,
getRandomUrl(), 5,
'http://www.example.com/Slide04.php', 5
);
var i = 0, len = frames.length;
function getRandomUrl()
{
return "http://www.example.com/Events.php?=" + (new Date().getTime());
}
function ChangeSrc()
{
if (i >= len) { i = 0; } // start over
document.getElementById('myiframe').src = frames[i++];
setTimeout('ChangeSrc()', (frames[i++]*1000));
}
window.onload = ChangeSrc;
</script>
The only trouble is everytime /Events.php is shown it has the same number appended to it so therefore shows the same post in each loop.
I need to append a different number to the /Events.php slide on each loop so it generates different content each time.
I'm starting to think I'm approaching this in totally the wrong way so any help or pointers in the right direction would be appreciated!
Cheers,
Mark.
The issue is you are only calling getRandomUrl() once which is when you defined your array, this means the value will always be the same as its only returned once.
One solution would be to store the function itself in your array like so:
var frames = Array(
'http://www.example.com/Slide01.php', 5,
'http://www.example.com/Slide02.php', 5,
getRandomUrl, 5,
'http://www.example.com/Slide04.php', 5
);
And then call it in ChangeSrc() if its a function
function ChangeSrc()
{
if (i >= len) { i = 0; } // start over
var frame = frames[i++],
isFnc = typeof(frame) == "function";
if(isFnc){
frame = frame();
}
document.getElementById('myiframe').src = frame;
setTimeout(function(){
ChangeSrc()
}, frames[i++]*1000);
}
http://jsfiddle.net/redgg6pq/
A tip would be that you are only calling 'getRandomUrl' once, hence why it's always the same image. You want to call it each time you are in the loop.
I would suggest removing it from the static array, and calling it in the loop - does that make sense? :)
HTH

Attempt to filter array items that were recently used

Array.prototype.spil = function (x,max) {
if (this.length===max) {
this.shift();
this.push(x);
} else {
this.push(x);
}
return this;
}
var heard = [0],
freash = [];
$('#speak').bind('touchstart', function() {
var sounds = [
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
];
var temp = sounds;
for (var i=0; i<heard.length; i++) {
temp.splice(temp.indexOf(heard[i]), 1);
}
freash = temp;
var say = Math.floor(Math.random() * freash.length+1) + 1;
heard.spil(say,10);
say = document.getElementById('c'+say); // audio element
// use the sound...
});
My attempt to make the sound that plays 'an un-recent one' is not working! I want to make the last sound that played not play again for 10 button clicks.
heard is the array of sounds that you have already heard, this has a Array.spil function which will fill the array via push until it reaches its max length of ten when it will shift then push instead.
freash is the array of sounds with no members of the heard array present.
You can see that heard and freash are declared outside of the click (touchstart) scope.
If I click/tap over-and-over sometimes I am hearing sounds that have been played already much sooner than I am suppose to, often following each other.
This has been bothering me for days but, I can't see the problem in my logic. Can you?
I think this line
var say = Math.floor(Math.random() * freash.length+1) + 1;
should be
var say = freash[Math.floor(Math.random() * freash.length)];
Also consider the shortcut
var say = freash[Math.random() * freash.length | 0];

JavaScript variables - new var altering original value

It's been a while since I've touched JavaScript, but something has got me flummoxed, simple I just know I should know it, but it's causing me a massive headache for the past hour!
Scenario;
Mop = {
els : [],
pool : [],
generate : function(){
for (var i=0; i<Mop.els.length; i++){
Mop.pool.push(i)
}
},
oneAtime : {
p : Mop.pool,
runit : function(){
timerID = window.setTimeout(function(){
Mop.show()
});
}
},
show : function(){
var p = Mop.oneAtime.p;
var r = Math.floor(p.length * Math.random());
p.splice(r,1);
},
init : function(){
Mop.els = $(".cells");
Mop.generate();
Mop.oneAtime();
}
}
Mop.init()
A long winded way to say it's a problem with variable assigning. But I thought I'd show the context.
In short, invoking Mop.show() affects both the arrays Mop.pool, and Mop.oneAtime.p, even though the latter was assigned Mop.pool value, and Mop.pool is not touched again. Mop.pool must remain un-altered.
I can hear someone laughing at how easy this must be - but I just cannot work out why both arrays are affected by the splice() method! Scope, closure, something like that I bet, I've tried all I can think of...

JavaScript Automated Clicking

So, here's my issue.
I need to write a script to be run in the console (or via Greasemonkey) to automate clicking of certain links to check their output.
Each time one of these links is clicked, they essentially generate an image in a flash container to the left. The goal here is to be able to automate this so that the QC technicians do not have to click each of these thumbnails themselves.
Needless to say, there needs to be a delay between each "click" event and the next so that the user can view the large image and make sure it is okay.
Here is my script thus far:
function pausecomp(ms) {
ms = ms + new Date().getTime();
while (new Date() < ms){}
}
var itemlist, totalnumber, i;
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""));
for(i = 0; i < totalnumber; i = i + 1) {
console.log(i);
itemlist[i].childNodes[1].click();
pausecomp(3000);
}
Now, totalnumber gets me the total number of thumbnails, obviously, and then itemlist is a list of get-able elements so I can access the link itself.
If I run itemlist[0].childNodes[1].click() it works just fine. Same with 1, 2, 3, etc. However, in the loop, it does nothing and it simply crashes both Firefox and IE. I don't need cross-browser capability, but I'm confused.
There is a built-in JS function "setInterval(afunction, interval)" that keeps executing a given function every "interval" miliseconds (1000 = 1s).
This fiddle shows how to use setTimeout to work through an array. Here is the code:
var my_array = ["a", "b", "c", "d"];
function step(index) {
console.log("value of my_array at " + index + ":", my_array[index]);
if (index < my_array.length - 1)
setTimeout(step, 3000, index + 1);
}
setTimeout(step, 3000, 0);
Every 3 seconds, you'll see on the console something like:
value of my_array at x: v
where x is the index in the array and v is the corresponding value.
The problem with your code is that your pausecomp loop is a form of busy waiting. Let's suppose you have 10 items to go through. Your code will click an item, spin for 3 seconds, click an item, spin for 3 seconds, etc. All your clicks are doing is queuing events to be dispatched. However, these events are not dispatched until your code finishes executing. It finishes executing after all the clicks are queued and (roughly) 30 seconds (in this hypothetical scenario) have elapsed. If the number of elements is greater that's even worse.
Using setTimeout like above allows the JavaScript virtual machine to regain control and allows dispatching events. The documentation on setTimeout is available here.
People were correct with SetInterval.
For the record, here's the completed code:
/*global console, document, clearInterval, setInterval*/
var itemlist, totalnumber, i, counter;
i = 0;
function findmepeterpan() {
"use strict";
console.log("Currently viewing " + (i + 1));
itemlist[i].scrollIntoView(true);
document.getElementById("headline").scrollIntoView(true);
itemlist[i].style.borderColor = "red";
itemlist[i].style.borderWidth = "thick";
itemlist[i].childNodes[1].click();
i = i + 1;
if (i === totalnumber) {
clearInterval(counter);
console.log("And we're done! Hope you enjoyed it!");
}
}
function keepitup() {
"use strict";
if (i !== 0) {
itemlist[i - 1].style.borderColor = "transparent";
itemlist[i - 1].style.borderWidth = "medium";
}
findmepeterpan();
}
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""), 10);
counter = setInterval(keepitup, 1500);

How to calculate the total duration of a set of tracks using the HTML5 audio?

I am building a custom audio player (planning phase for now :) ), and I am stuck with this. I wanna calculate the total duration of all my songs in the beginning. I know that I need to load each song in order to know its duration. If I set the src attribute for instance for the first song I get the duration of it with the loadedmetadata event handler. But how to get the sum of them all? Do I need to load each one individually? Do I need to set the src attr of the audio element for each song I have in the list? Any hints would be much appreciated!
I was here yesterday because I needed to solve this exact same problem and, of course, StackOverflow was top of the search results - even for this four year-old unanswered questions. Alas my searches for a native JS solution proved fruitless, though I tried several different suggestions from here and there, so here's what I eventually came up with myself...
function tracksTotalLength(Tracklist) {
var tempAudio = document.createElement('audio'),
L = Tracklist.length,
i = 0, // iterator
TTL = 0, // TotalTrackLength
flag = true;
// set media-type and mute (belt and braces)
tempAudio.type = 'audio/mpeg';
tempAudio.muted = true;
tempAudio.addEventListener(
'durationchange',
function(e) {
TTL += e.target.duration;
/* DEBUG ONLY */ console.log('TTL:',TTL);
recursiveUpdater(i++);
},
false
);
function recursiveUpdater(ii) {
if (!isNaN(TTL) && ii < L) {
tempAudio.src = Tracklist[ii];
// fires recursiveUpdater()
// via 'durationchange' event
} else {
// sometimes not all values are caught
// and TTL = NaN, so
// reset and have another run at it
if (isNaN(TTL) && flag) {
i = 0;
flag = false;
recursiveUpdater(i);
} else (
// but you can't recurse all day so
// remove tempAudio
tempAudio = null;
// if it hasn't caught return false
return (!isNaN(TTL) : TTL : false);
}
}
}
return recursiveUpdater(i);
}
var tracks = [
'path/to/track01.mp3',
'path/to/track02.mp3',
'path/to/track03.mp3',
/*...etc... */
];
var totalLength = tracksTotalLength(tracks);
//=> totalLength = seconds (float) or false
The function tracksTotalLength() returns the total time of the list in seconds which can then be rounded into hh:mm:ss as per this classic StackOverflow answer.
The 'durationchange' is fired when the metadata is loaded or changed. More on this at Media Events (MDN)
Tested in latest Firefox and Chromium browsers on Lubuntu 14.04 with tracklists of 5-50 items.
Hope this helps someone, or at least encourages a true JS guru to come up with a better routine.

Categories

Resources