I'm facing a problem with Icefaces and it's javascript bridge.
I don't know what are the changes which made by this bridge after i made a changes in the server-side.
For example: I have a ice:panelPopup component in my page with the visible attribute = "#{bean.customPopUp}". If i changed the "bean.customPopUp" to be "true" the popup is displayed correctly, but what i need to know : what happened in the client, in other word, i need to know if the popup is displayed i need to do some client processing using javascript
I've been trying to find a solution for component level callbacks also. There doesn't appear to be a good solution to this problem. I've resorted to initiating a recursive polling function in Javascript that handles my task after it detects an update to my component. My backing bean starts the poller() and it runs every 500ms until the component update has occurred.
var pollingCount = 0;
var previousValue;
function poller() {
// Kill poller after 30 seconds
if (pollingCount >= 60) {
pollingCount = 0;
return;
}
var currentValue = document.getElementById('myInputElement').value;
if (previousValue != currentValue) {
previousValue = currentValue;
pollingCount = 0;
myFunction();
}
else {
pollingCount++;
setTimeout('poller()', 500);
}
}
My backing bean:
updateDataModel(); // Causes 'myInputElement' component to update
FacesContext fc = FacesContext.getCurrentInstance();
JavascriptContext.addJavascriptCall(fc, "poller();");
I don't like this solution very much, but there don't appear to be any great answers at this time.
Related
When events are queued with setTimeout/setInterval, and the user is viewing a separate tab, Chrome and Firefox enforce a minimum 1000ms lag before the event is executed. This article details the behaviour.
This has been discussed on StackOverflow previously, but the questions and answers only applied to animations. Obviously, an animation can just be forced to update to the latest state when a user re-enters the tab.
But the solution does not work for sequenced audio. I have Web Audio API playing several audio files in sequence, and setTimeout is used to countdown to when the next audio file plays. If you put the tab in the background, you get an annoying 1 second gap between each pattern -- an extreme flaw in an API designed for advanced audio.
You can witness this behaviour in various HTML5 sequencers, e.g. with PatternSketch -- just by entering a pattern, playing, and going to another tab.
So I'm in need of a workaround: a way to queue events without the 1000ms clamp. Does anyone know of a way?
The only solution I can think of is to have window.postMessage run every single millisecond and check each time if the event is to execute. That is definitely detrimental to performance. Is this the only option?
Apparently there is no event system planned for Web Audio API, so that is out of question.
EDIT: Another answer is to use WebWorkers per https://stackoverflow.com/a/12522580/1481489 - this answer is a little specific, so here's something more generic:
interval.js
var intervalId = null;
onmessage = function(event) {
if ( event.data.start ) {
intervalId = setInterval(function(){
postMessage('interval.start');
},event.data.ms||0);
}
if ( event.data.stop && intervalId !== null ) {
clearInterval(intervalId);
}
};
and your main program:
var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback() {
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};
var doWork = new Worker('interval.js');
doWork.onmessage = function(event) {
if ( event.data === 'interval.start' ) {
stuff.callback(); // queue your custom methods in here or whatever
}
};
doWork.postMessage({start:true,ms:250}); // tell the worker to start up with 250ms intervals
// doWork.postMessage({stop:true}); // or tell it just to stop.
Totally ugly, but you could open up a child popup window. However, all this does is transfer some of the caveats to the child window, i.e. if child window is minimized the 1000ms problem appears, but if it is simply out of focus, there isn't an issue. Then again, if it is closed, then it stops, but all the user has to do is click the start button again.
So, I suppose this doesn't really solve your problem... but here's a rough draft:
var mainIntervalMs = 250;
var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback(){
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};
function openerCallbackHandler() {
stuff.callback(); // queue your custom methods in here or whatever
}
function openerTick(childIntervalMs) { // this isn't actually used in this window, but makes it easier to embed the code in the child window
setInterval(function() {
window.opener.openerCallbackHandler();
},childIntervalMs);
}
// build the popup that will handle the interval
function buildIntervalWindow() {
var controlWindow = window.open('about:blank','controlWindow','width=10,height=10');
var script = controlWindow.document.createElement('script');
script.type = 'text/javascript';
script.textContent = '('+openerTick+')('+mainIntervalMs+');';
controlWindow.document.body.appendChild(script);
}
// write the start button to circumvent popup blockers
document.write('<input type="button" onclick="buildIntervalWindow();return false;" value="Start" />');
I'd recommend working out a better way to organize, write, etc. but at the least it should point you in the right direction. It should also work in a lot of diff browsers (in theory, only tested in chrome). I'll leave you to the rest.
Oh, and don't forget to build in auto-closing of the child window if the parent drops.
Somebody posted this yesterday on gitter. I ws running into the same issue lately. since this is a common usecase i just post his question here again so it doen't get lost in the gitter universe...
Is there a simple way to force a ValueConverter to always call toView for every fromView? I have an that I'm using a ValueConverter with to clamp to integers only. It works, but with a strange twist: If I change it from 0 to 1.5 and click away (also using updateTrigger:'blur' here), it correctly stores 1 in the VM and then changes the input value to 1 as well. But if I change it from 1 to 1.5, it updates the VM, but since the value in the VM doesn't appear to change, it doesn't update the input (no call to toView).
I know I could solve this with an event listener and signal, but that feels like overkill.
thanks
better to use a binding behavior if you want to add "behavior". This will give you more control, you could even control the cursor, etc.
https://gist.run?id=3d2870889cbf245e652f53db28d8476b
integer-input-binding-behavior.js
export class IntegerInputBindingBehavior {
bind(binding, source) {
binding.standardUpdateSource = binding.updateSource;
binding.updateSource = function(value) {
const intValue = parseInt(value, 10);
if (isNaN(intValue)) {
this.standardUpdateSource(0);
return;
}
this.standardUpdateSource(intValue);
if (intValue.toString(10) !== value) {
this.updateTarget(intValue.toString(10));
}
};
}
unbind(binding, source) {
binding.updateSource = binding.standardUpdateSource
binding.standardUpdateSource = null;
}
}
Better still to create a custom element.
Use the signal binding behavior
You'll see in the issue you linked that I had commented with a similar question. The solution I implemented was to use the signal binding behavior.
template.html
<path d="${path| toSVGPath & signal: 'update-view'}"
style="stroke: black; stroke-width: 5; fill: none;"></path>
viewModel.ts
import {BindingSignaler} from 'aurelia-binding';
// grab a reference to the signaler
constructor(signaler: BindingSignaler) {
this.path = [[0,0]];
this.signaler = signaler;
}
// and fire the signal event bound in your view
// whenever the data behind the valueConverter changes
updatePath(position) {
this.path.push(position);
this.signaler.signal('update-view');
}
I am currently working on a drum machine and I am using setTimeout to make it run. Here is the heart of the code:
var sequencerRun = function(){
var currentTime = 0
var starting = 200;
for(var k = 0; k < 16; k++){
$(".instrument td .beat" + k).each(function(){
setTimeout(blinker, currentTime,$(this));
})
currentTime += starting;
}
}
var timerId, setInt;
var runSeq = function(){
setInt = setInterval(sequencerRun,3200);
}
$('.play').click(function(){
stopped = false
sequencerRun();
runSeq();
})
$('.stop').click(function(){
clearInterval(setInt);
stopped = true;
})
The drum machine has a matrix HTML structure built using a table. When .play is clicked a scheduling process occurs, which is encapsulated in sequencerRun. This involves a run through the columns of my matrix to determine whether there should be a drum hit or not. This is done through blinker. The scheduling creates a check on each column 1 - 16 at times 0,200,...,3200 respectively. This is what creates the effect of a sequencer. I also have a setInterval that reruns this process every 3200, which is how it takes for a run to finish.
Programmatically my code seems to make sense and my hope was that it would execute on time. The thing is that my actual app tends to stutter a lot and is stuttering even more since I deployed it. Here is a deployed version of my app.
This stuttering side effect can be best heard when you click on a full row. My question here is can anyone tell if this side effect is a result of setTimeout's timing inconsistency and if so how could I go about fixing this? Or is this related to something else that I am missing?
I think the stuttering issue has more to do with you not preloading the instruments but rather loading them on every hit, more than it has to do with settimeout.
In any case, I think I would have solved this differently. Rather than setting a fresh timeout for each beat, create one beat timeout and put the logic in there. Something like (pseudo-code-ish, lots of stuff missing just the general idea):
var MAX_BEATS = 16; // I think you had 16 beats in your example?
var BPM = 200;
var preloadedInstruments = [];
function preloadInstruments(){
for(i of myInstruments) { // myInstruments would be a list of your instruments (probably just strings with a filename)
preloadedInstruments.push(new instrument(i)); // You need an instrument class, that would preload the sound and provide a play function
}
}
var currentbeat = 1;
function beat() {
var activeInstruments = getActiveInstruments(currentbeat); // You could cache this also for every beat, but I think a simple loop here would be quick enough
for(instrument of activeInstruments) {
preloadedInstruments[instrument].play(); // play method of the instrument class called
}
currentbeat++;
if (currentbeat > MAX_BEATS) {
currentbeat = 1;
}
}
setInterval(beat, 60e3 / BPM);
I have a function that performs a long task. I would like to create a function that is able to notify the caller of the progress. Ultimately I want to update the UI with the current progress.
Something like this:
function myLongMethod(progressCallback)
{
for(var i = 0 ... )
{
progressCallback(i) ;
}
}
This works but updates on UI are not smooth. Is there a better way? I would prefer something with a jquery Deferred object using deferred.notify(). Any ideas?
Your code is fine. You have got another problem. Javscript always runs on the UI thread. Your operation is blocking this thread (the browser) and you will see some blocking of your browser window.
Luckily there is a workaround implemented in modern browser called web workers. It's simple just call in your main script another script which then get executed:
var w = new Worker("another_script.js");
If your worker is ready you can react on the result by adding a event listner to the worker:
w.onmessage = function(event) {
//do something
}
When you use this pattern, your UI did not block. You can even return data from a web worker and include scripts into it. More details you can find here and here is a good starting tutorial.
Hi you can apply the easing effect to your UI for smoothness and i am giving the following code it may help you
var oldProgress = 0;
var uiUpdater = null;
function updateUI(newProgress){
if(uiUpdater !=null){
// update your ui to the old progress first
window.clearInterval(uiUpdater); // clearing the previous timer
}
var diff = newProgress - oldProgress;
oldProgress = newProgress;
var stepSize = diff/5; // applying the new change in 5 steps to the UI
uiUpdater = window.setInterVal(function(){
// updating your UI after every 100 milliseconds
// to give the smoothness
diff -= stepSize; // decreasing the difference gradually
if(diff<=0){
window.clearInterval(uiUpdater); // clearing the interval once the update is done
}
},100);
}
You have to call the "updateUI" method from you callback with the new progress.
I am trying to make a basic enough page that allows the user to execute a php script by clicking a button. Each button will have a loading spinner popup on clicking.
My problem is, on clicking one button and then clicking another, both spinners close at the exact same time even though the second may still be processing.
Does anyone know how to make these spinners truly asynchronous ? Thanks so much in advance, its killing me.
JS:
function test(element){
var append = "#";
var test = append.concat(element);
document.getElementById(element).style.visibility='visible';
$.ajax({url:"test.php",success:function(result){
hide(element);
}
});
};
function hide(element){
document.getElementById(element).style.visibility='hidden';
};
</script>
HTML:
<html>
<?
$index = 0;
$myArray = array ("1", "2", "3", "4", "5");
for($index = 0; $index < 5; $index++){?>
<button onclick="test('<?echo $myArray [$index];?>')">Start</button>
<img id="<?echo $myArray [$index];?>" src="images/loader.gif"
style="visibility:hidden"/>
<br><br>
<?}?>
</html>
I would implement a counter. Each time you show the loading indicator, add one to the counter and each time you want to hide it, subtract one. Then monitor the counter and whenever it is above zero show the loading indicator and when at zero hide it. Make sense?
Something like the following (untested) code might do the trick and it neatly means you can avoid worrying about the spinner at all in ajax requests:
var spinningAjax = (function() { // use of the closure created by an immediate function gives us the scope to create a persistant counter variable
var counter = 0;
$(document).ajaxComplete(function() {
counter--;
if (counter === 0) {
showSpinner(false);
}
});
return function(settings) {
counter++;
showSpinner(true);
$.ajax(settings);
}
})();
var showSpinner(bool) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};
EDIT: Ok, having seen the comments to another answer, I realise this doesn't quite solve the situation you're in. I'll have a think and see if I can do better
EDIT2: I think this (still untested, unfortunately) code may be what you require. Please let me know in the comments if you have any issues.
var spinningAjax = (function() { // closure of immediate function lets us create a persistant array of the counters for each spinner
var counter = []; // an array to hold the counters for each spinner
$(document).ajaxComplete(function(event, xhr, settings) { // called whenever any ajax request is completed
if (typeof settings.ajaxGroup !== 'undefined') { // only update the counters if an ajaxGroup has been provided
counter[settings.ajaxGroup]--;
if (counter[settings.ajaxGroup] === 0) {
showSpinner(false, settings.ajaxGroup); // hide spinner when all requests connected with the spinner have been completed
}
}
});
return function(settings) { // this is the function actually assigned to the variable spinningAjax as a result of the immediate function
counter[settings.ajaxGroup] = counter[settings.ajaxGroup] ? counter[settings.ajaxGroup]+1 : 1; // can't just use the ++ operator as this property might not be defined yet
showSpinner(true, settings.ajaxGroup);
$.ajax(settings);
}
})();
var showSpinner(bool, spinnerIdentifier) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};