Prevent JQuery .HTML overwriting previous .HTML before its displayed - javascript

I am currently using a multi-dimensional array to store values and then loop round it based on if it meets two requirements does a number match and does it have a set state E.G 487 Online.
This changes in real time based on what event happens to be caught and inserted in the MD array on the server idea and everything is grand on that side of things.
Am trying to visual represent each state of a call in the following format:
If phone device is online turn tile red otherwise turn green and do .HTML.
If phone is ringing turn tile yellow and do .HTML.
If phone is in a call then turn the tile orange and do .HTML.
If phone just hangup then and do .HTML (but it doesn't do this) then turn blue via a css class using .addClass......then remove it after 3 seconds. (It wont display otherwise because status online gets caught right after so you dont even see it turn blue hence using a setTimeout to stop this.)
Steps 1 to 3 work accordingly the issue comes in with step 4.
The issue am having with this is am using JQuery .HTML to overwrite content in the tile every time the phone changes state but after some debugging and fiddling around I have found that the issue seems to be JQuery .HTML and/or how am using it and the setTimeOut I think but I don't know why.
Problem is the Hangup event gets caught........ .HTML here doesn't seem to display and seems to get overwritten by the .HTML in the online event which gets caught straight after hangup and therefore displays .html from the online event instead.
I found when I remove the .HTML at step 1 it stops the issue, but I need it in step 1. But if I leave it there it seems to be overwriting the .HTML in step 4 which I also need for some reason.
Array looks like this with the extension number, SIP device status, current state the device is in e.g Ringing:
[ '487', 'online', 'Hangup' ],
[ '488', 'online' ],
[ '477', 'online', 'Hangup' ] ]
What the server side events generally look like at the point where the event is caught:
477 and 487 both hungup
[ [ '487', 'online', 'Hangup' ], [ '477', 'online', 'Hangup' ] ]
something happened to a ChannelDestroyed
Channel Output ChannelDestroyed
something happened to an endpoint
EndpointStateChange
477 current state is: online
[ [ '487', 'online', 'Hangup' ], [ '477', 'online', 'Hangup' ] ]
Here is my code at the moment:
//Handles events to signal current call states aka if 488 dials 487 highlight both tiles with those values.
socket.on("eventsCalls", function (calldata) {
for (var i = 0; i < calldata.length; i++) {
if (calldata[i][0] && calldata[i][2] === "Ringing") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").css("background-color", "yellow").append("<h4>User</h4><p>" + calldata[i][0] + " Is Ringing</p>");
} else if (calldata[i][0] && calldata[i][2] === "Hangup") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").html("<h4>User</h4><p>" + calldata[i][0] + " just hungup</p>").addClass("hangup");
setTimeout(function () {
$("div[class*='tile']").removeClass("hangup");
}, 3000);
} else if (calldata[i][0] && calldata[i][2] === "ANSWER") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").css("background-color", "orange").html("<h4>User</h4><p>" + calldata[i][0] + " Is Busy</p>");
}
}
});
// Handles which sip devices are currently registered and displays them on the web page.
socket.on("eventsRegister", function (regisdata) {
for (var i = 0; i < regisdata.length; i++) {
if (regisdata[i][0] && regisdata[i][1] === "online") {
$("div[class*='tile']:contains('" + regisdata[i][0] + "')").css("background-color", "green").html("<h4>User</h4><p>" + regisdata[i][0] + " Online</p>");
} else if (regisdata[i][0] && regisdata[i][1] === "offline") {
$("div[class*='tile']:contains('" + regisdata[i][0] + "')").css("background-color", "red").html("<h4>User</h4><p>" + regisdata[i][0] + " Offline</p>");
}
}
});
Is there any better alternatives or workarounds for this issue am having?
EDIT: JSFiddle for an idea of what am trying to do its hard to replicate the catching of events but when it hits hangup the .html seems to be overwritten by Online event which seems to happen at the same time I think. It doesn't work like that for the last two events on my code the colors change but the .HTML for hangup seems to be overwritten by online.

I can't say for certain, but your setTimeout is missing a delay parameter.
var hangupDelay = 5000; // 5 seconds.
setTimeout(function () {
$("div[class*='tile']").removeClass("hangup");
}, hangupDelay );
This should remove the hangup class after 5 seconds anyway.
If you do not specify the setTimeout delay param; you get it almost instantly:
https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
delay is the number of milliseconds (thousandths of a second) that the
function call should be delayed by. If omitted, it defaults to 0. The
actual delay may be longer; see Notes below.
This might result in the hangup class instantly being removed and appearing to not be working.

Related

WebAudioAPI sometimes drops scheduled fade values

Ehy everyone,
I'm developing a musical web-application using the Web Audio API, and I'm facing a problem that I cannot understand.
The app basically works like this: the user navigates through a full-screen image, on which there are some specific points associated with specific sounds. The app plays the sounds that are closer to the user, and fade them out when the user gets away.
The problem occurs (not every time) when the user is in a position where there are more than two sounds (3 or 4, but sometimes also 2 is enough) playing, and then goes in a "silent" position.
In that case the fades are somehow dropped and the sounds are abruptly interrupted. I can see from the logs that it enters the fading function, but it doesn't execute the scheduled gain curves.
To be more clear, here's an example of the steps my algorithm does:
when the user is in the position A, let's say 3 sounds are playing;
the user moves to position B and an event is fired when the action is finished;
the algorithm calculates how many sounds are associated to position B: zero;
performs a comparison with sounds in position A and calculates that sound 1, 2 an 3 are gone and they need to be stopped with a fade out;
for each sound invokes fadeOut function, which schedules fade values with WebAudioAPI methods, and schedules stop action with setTimeout();
The result is that the sound remains with maximum volume till the stop function is invoked.
Here's the fadeOut() function:
StreamSound.prototype.fadeOut = function (fadeTime) {
const self = this;
if (self.isPlaying()) {
if (!fadeTime) {
console.warn(`Missing argument fadeTime. Set to 1 second`);
fadeTime = 1;
}
var currGain = self.masterGain.gain.value;
var curTime = self.context.currentTime;
self.masterGain.gain.cancelScheduledValues(curTime);
self.masterGain.gain.setValueAtTime(currGain, curTime);
self.masterGain.gain.linearRampToValueAtTime(0.0001, curTime + fadeTime);
self.fading = true;
setTimeout(function () {
// If has not secondary streams
if (self.secondaryStream === undefined) {
console.log(`No secondary streams -> stop`);
self.pause();
} else if (!self.secondaryStream.isPlaying()) {
console.log(`Not playing secondary streams -> stop`);
self.pause();
} else {
self.playing = false;
}
self.fading = false;
console.log(`Fade out stream finished [ ID: ${self.index} ]`);
}, fadeTime * 1000 + 25);
console.log(`Fading out stream [ ID: ${self.index} ]`);
} else {
console.warn(`Cannot fade out. Sound not playing [ ID: ${self.index} ]`);
}
};
I suppose that probably that's due to scheduling issues, or can it be something related with JS thread management?
I know is not easy to help me just with these informations, but maybe someone had a similar problem.
Thanks in any case,
Francesco
EDIT: I opened another post with a more general description and also a link to the source code and an online beta version ->
WebAudioAPI musical application: clicks and crackles and abrupt stops

Why is some of my code running before other ones?

So i was running my script and i was wondering why some of my code was running before eachother.
For example, i had a function, and i called it last, AFTER a message was supposed to be displayed. But instead it ran the function first THEN displayed the message.
Also, i was noticing with displaying messages, it would display in a random order each time. (Wierd Huh.)
I managed to fix some of this by creating a bunch of .this's, but i don't want to do that so much in the future as it makes code very confusing.
Here is a snippet of some of my code that this is happening to:
else if (input.startsWith('CALL')) {
//First
bot.sendMessage(message, ':arrow_right: Private Messaging You To Decrease Spam :arrow_left:');
//Second
bot.sendMessage(user.id, ':rotating_light: This feature is experimental. You may experience bugs/glitches when using it. :rotating_light: \n').then(() => getUser()); // << Third
var senderId = message.sender.id; // << Fourth
function getUser() {
try {
var userList = []
for (var user of bot.users) {
if (user.status == "online" && user.bot == false) {
userList.push(user)
}
}
bot.sendMessage(senderId, ':telephone: :arrow_right: Users Found That Meet Search Criteria: ' + userList.length + ' out of ' + bot.users.length + '.'); //Fifth
bot.sendMessage(message, ':telephone: :arrow_right: User Found: ' + userList[Math.floor(Math.random() * userList.length)]); //Sixth
} catch (err) {
bot.sendMessage(message, err)
}
console.log(userList);
console.log(userList.length);
}
}
The code works, it's just the order i was worried about.
Thanks in advance for the help :)
EDIT: I added comments to say what the order should be.
EDIT #2: Clarified Question: I was wondering how i would be able to make the code run in a certain order. And wait for a code prior to run first.
I assume bot.sendMessage makes a network request of some sort to some other system, and the issue you're observing is that those network requests seem to take effect in an unpredictable order. This makes sense, because you're not waiting for one request to finish before starting the next one.
Imagine you opened a web browser and opened two tabs to two different URLs at the same time. Which page would load first? That's basically what your code is doing.
Because I see a .then in one place, I'm going to further assume that bot.sendMessage returns a promise. So go ahead and use the promise to wait for the first request to finish before starting the next one.
Specifically, change this:
bot.sendMessage(senderId, ':telephone: :arrow_right: Users Found That Meet Search Criteria: ' + userList.length + ' out of ' + bot.users.length + '.');
bot.sendMessage(message, ':telephone: :arrow_right: User Found: ' + userList[Math.floor(Math.random() * userList.length)]);
to this:
bot.sendMessage(senderId, ':telephone: :arrow_right: Users Found That Meet Search Criteria: ' + userList.length + ' out of ' + bot.users.length + '.').then(() => {
bot.sendMessage(message, ':telephone: :arrow_right: User Found: ' + userList[Math.floor(Math.random() * userList.length)]);
});
(Ditto with your other messages that you want to happen in a certain order.)
the function you use to send the message runs asynchronously, so you must wait for the end of the first call before invoke the next one (and the rest of the code). smarx gave you a good example about what to do.

Update DOM Elements Immediately in Javascript

For a simple project, I'm trying to display log output in a textarea. I have a log function, and would like the textarea to always be updated immediately whenever log is called. Unfortunately, it seems to only do the update once, and push everything at the same time (I'm using JQuery to set the value of the textarea, if that possibly matters). Here's a basic example:
for (i = 0; i <= 100; i++) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(9999999)
};
This spits out all the text into the textarea at once (note: you can see the results of these examples at this jsfiddle). This basic example is easily remedied by creating a timeout and using recursive function calls:
f = function(i) {
if (i <= 100) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(999999);
setTimeout(function() { f(i+1); }, 0);
}
};
f(1);
This version spits out the text into the textarea one line at a time, which is what I want. But using timeouts and callbacks in this manner does not seem practical in the setting of logging; every time I call log, I would have to provide a callback for all the functionality that I want to ever follow the log call.
Is there any way to achieve the desired effect without callbacks?
I think you might consider using :
$("textarea").val(function(index, value) {
return value + "test" + i + "\n"
});
instead of :
$("textarea").html($("textarea").html() + "test" + i + "\n");
or in general :
$("textarea").val(NEWVAL)
Also noted in the comments of your question, if you want to be able to notice "by eye" all the messages that arrives you'll have to save them in a buffer and have something like (not tested) :
var buffer = []
function log(text) { buffer.push(text) }
setInterval(function(){
if (len(buffer)>0) {
$("textarea").val(function(index, value) {
return value + "\n" + buffer.shift()
});
}
},500)
Browsers generally (Opera is an exception) do JS execution and rendering on the same thread, so while your JS is running there will be no rendering done by th browser, unless you explicitly yield control via a timeout or interval timer.

How to tell if an image has loaded correctly

Is there a way to tell, after the fact, whether an image (placed with the <img> tag, not via JS) has loaded correctly into a page? I have a gallery of head shots, and occasionally the third-party image server ends up serving up a 404. I can change the server-side code to use an onerror="showGenericHeadshot()", but I really want to avoid making changes to server-side code. Ultimately, I want to determine if an image is missing or broken and replace it with a generic "Image Not Found" graphic. Things I've tried:
Image.prototype.onerror = showGenericHeadshot -- doesn't work for <img> tags
$('img[src*=thirdpartyserver.com]).error(showGenericHeadshot) -- doesn't work in IE
$('img[src*=thirdpartyserver.com]).css('backgroundImage','url(replacementimage.gif)') -- works, but still doesn't get rid of the broken image icon in IE
<img scr='someUrl' id="testImage" />
jQuery('#testImage').bind('load',function(){
alert ('iamge loaded');
});
to avoid race condition do as below
<img _src="http://www.caregiving.org/intcaregiving/flags/UK.gif" />
// i have added an underscore character before src
jQuery('img').each(function(){
var _elm=jQuery(this);
_elm.bind('load',_imageLoaded).attr('src',_elm.attr('_src'))
});
function _imageLoaded()
{
alert('img loaded');
}
Unfortunately, I'm not able to accept either #TJ Crowder's nor #Praveen's excellent answers, though both do perform the desired image-replacement. #Praveen's answer would require a change to the HTML (in which case I should just hook into the <img> tag's own error="" event attribute. And judging by network activity, it look like if you try to create a new image using the url of an image that just 404ed in the same page, the request actually does get sent a second time. Part of the reason the image server is failing is, at least partly, our traffic; so I really have to do everything I can to keep requests down or the problem will only get worse..
The SO question referred to in #danp's comment to my question actually had the answer for me, though it was not the accepted answer there. I'm able to confirm that it works with IE 7 & 8, FF and webkit browsers. I'm doubtful it will work with older browsers, so I've got a try/catch in there to handle any exceptions. The worse case will be that no image-replacement happens, which is no different from what happens now without doing anything. The implementation I'm using is below:
$(function() {
$('img[src*=images.3rdparty.com]').each(
function() {
try {
if (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0))) {
this.src = 'http://myserver.com/images/no_photo.gif';
}
} catch(e) {}
}
);
});
Would an alternate text be sufficient? If so you can use the alt attribute of the img tag.
I think I've got it: When the DOM is loaded (or even on the window.load event — after all, you want to do this when all images are as complete as they're going to get), you can retroactively check that the images are okay by creating one new img element, hooking its load and error events, and then cycling through grabbing the src from each of your headshots. Something like the code below (live example). That code was just dashed off, it's not production quality — for instance, you'll probably want a timeout after which if you haven't received either load or error, you assume error. (You'll probably have to replace your checker image to handle that reliably.)
This technique assumes that reusing a src does not reload the image, which I think is a fairly reliable assumption (it is certainly an easily testable one) because this technique has been used for precaching images forever.
I've tested the below on Chrome, Firefox, and Opera for Linux as well as IE6 (yes, really) and IE8 for Windows. Worked a treat.
jQuery(function($) {
var imgs, checker, index, start;
// Obviously, adjust this selector to match just your headshots
imgs = $('img');
if (imgs.length > 0) {
// Create the checker, hide it, and append it
checker = $("<img>").hide().appendTo(document.body);
// Hook it up
checker.load(imageLoaded).error(imageFailed);
// Start our loop
index = 0;
display("Verifying");
start = now();
verify();
}
function now() {
return +new Date();
}
function verify() {
if (!imgs || index >= imgs.length) {
display("Done verifying, total time = " + (now() - start) + "ms");
checker.remove();
checker = undefined;
return;
}
checker[0].src = imgs[index].src;
}
function imageLoaded() {
display("Image " + index + " loaded successfully");
++index;
verify();
}
function imageFailed() {
display("Image " + index + " failed");
++index;
verify();
}
function display(msg) {
$("<p>" + now() + ": " + msg + "</p>").appendTo(document.body);
}
});​
Live example

Bypass all cacheing on jQuery.autocomplete(1.02)

I am using jQuery.autocomplete(1.02) on my search box and I want exact string and substring matching. I don't care (yet!) about the database load, I'm happy for it to fire off a query every keystroke and bypass the caching entirely - I just don't want anything missed.
To this end I have tried setting cacheLength=1, the minimum permitted, but the autocomplete function refuses to fire off a GET request for each key up.
searchbox GET_request
'a' -> http://localhost/service_search_request?q=a
'ar' -> http://localhost/service_search_request?q=ar
'ars' -> http://localhost/service_search_request?q=ars
Instead, it sends the first and the third and misses the second, giving me the wrong results for 'ar' :-/ I've cleared my cache and sessions but it looks like some sort of caching is still going on. AFAIK I have no proxying going on and I'm shift-refreshing each time. It looks likely then that this behavior is from jQuery.autocomplete itself.
So my questions are...
A) Does this seem likely? i.e. is it a feature, or maybe a bug?
B) If so is there a clean way around it?...
C) If not, what autocomplete would you use instead?
Naturally D) No you're just using it incorrectly you douche! is always a possibility, and indeed the one I'd prefer having spent time going down this road - assuming it comes with a link to the docs I've failed to find / read!
Cheers,
Roger :)
I wonder why cacheLength doesn't work, but had trouble with autocomplete too. IMHO, there are errors in it. However, in the list of options, there is a matchSubset you could set to false.
EDIT:
somewhere around line 335 is a function called "request". You could add some debug messages to it, to see what happens: (note: you need firebug installed or "console" will be unknown)
function request(term, success, failure) {
console.debug("ac request...");
if (!options.matchCase)
term = term.toLowerCase();
var data = cache.load(term);
console.debug("ac request 1, loaded data from cache: " + data + " term: " + term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
console.debug("ac request 2, data is not in the cache, request it");
"flushCache" can easily be used in the function you can attach / set as options. I used this, to clear the Cache, if there could be more data in the backend:
formatItem: function (data,i,n,value){
if(i === (this.max -1)){
console.debug("flushCache");
jQuery(this).flushCache();
}
return data[1] + " (" + data[0] + ")";
}
I am having the same problem. Caching doesn't work although I have set the option cacheLength to 1.
With your solution to call the flushCache function after each printed term it works. I couldn't use the:
if(i === (this.max -1)){
since 'i' was e.g 1 after filtering but 'this.max' still 25 as the original backend query resulted in 25 returned rows.
However, this bug ONLY appears when typing words that contain the swedish characters 'å', 'ä' or 'ö'. So maybe the cashing works as expected but not with these special characters.
Anyway. the solution for me was to always call the flushCache control in the formatItem() function:
function formatItem(row, position, n, term) {
if($("#keywords-h").length > 0){
$("#keywords-h").flushCache();
}
// format Item
return "<span>" + row[0] + "</span>";
}
Hope this helps someone and if someone is having the same problems with special characters please post a reply.
Have obviously come to this 18 months on, but
cacheLength: 0
in the options worked for me. So maybe latest release has fixed the bug?
This worked for me.
function requestData(q) {
if (!options.matchCase) q = q.toLowerCase();
//-- I turned off this line
// var data = options.cacheLength ? loadFromCache(q) : null;
//-- And added this line of code
var data = null;
There is an option to disable subset matching e.g.
$("#query").autocomplete(
url,
{
matchSubset: false
}
)

Categories

Resources