Does anyone know what's the replacement method for FB.CanvasClient.startTimerToSizeToContent() to resize iframe content in the new javascript SDK for Facebook?
Or maybe if there is an alternative technique for achieving the same effect?
Thanks
Examples
This function is useful if you know your content will change size, but you don't know when. There will be a slight delay, so if you know when your content changes size, you should call setSize yourself (and save your user's CPU cycles).
window.fbAsyncInit = function() {
FB.Canvas.setAutoResize();
}
If you ever need to stop the timer, just pass false.
FB.Canvas.setAutoResize(false);
If you want the timer to run at a different interval, you can do that too.
FB.Canvas.setAutoResize(91);
Note: If there is only 1 parameter and it is a number, it is assumed to be the interval.
Parameters
Name Type Description
onOrOff Boolean Whether to turn the timer on or off. true == on, false == off. default is true
interval Integer How often to resize (in ms). default is 100ms
Related
I have a chrome app that I want to correctly resize (dimensions proportional to the screen width) on resolution change. I have written a function that redraws the app with the correct dimensions, now I want to execute it only when it needs to.
A resolution change causes screen.width to change, but (probably unsurprisingly since they relate to different things) the "resize" event is not fired, and as far as I can tell no event is fired.
I know about the Proxy object (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) so I wrote some code which detects when a variable is set and executes my callback, this seemed to work but not in the instance of resolution change.
So I searched online and tried this https://gist.github.com/eligrey/384583 (which is essentially the answer provided in several stackoverflow questions on a similar topic, and indeed what I initially produced, albeit lacking the abstraction that the newish Proxy object offers).
This also seems to work (in the sense if I say manually set screen.width after having done screen.watch("width", () => console.log("hello")); and then do screen.width = 100; my callback is executed). But not in the instance of resolution change (in fact, perhaps most importantly, assigning this watcher seems to prevent screen.width getting assigned).
I have three questions
1) What is going on when I assign a watcher/proxy that's messing things up.
2) How could I find out what the browser is doing at this level of detail (what sends the trigger to change screen.width? I guess it is the OS, what does this look like)
3) Is there a better way to achieve what I was initially going for (the chrome app resizing).
Mostly I am interested in question 1) and don't care much about 3) any more.
To replicate my issue,
open a new tab in firefox or chrome
go to the developer console.
check screen.width, change resolution, observe that screen.width changes
Copy and paste the code from https://gist.github.com/eligrey/384583
Do screen.watch("width", (id, oldval, newval) => {console.log("hello"); return newval;});
Do screen.width = 100; and observe that hello is logged and screen.width is set to 100
Change resolution, observe that screen.width is not set.
Edit - As revealed after Bertrand's answer, the resize event may actually fire on resolution change, but this seems to be as a response to the resolution change forcing the boundary of the the window to get smaller, if the window is small then screen.width can change without firing a resize event.
What is going on when I assign a watcher/proxy that's messing things up.
The issue is that the width property is actually a getter, on Screen.prototype. It's not an ordinary value on window.screen:
console.log(window.screen.width);
console.log(window.screen.hasOwnProperty('width'));
// The property exists on the prototype instead, and is a getter:
const descriptor = Object.getOwnPropertyDescriptor(Screen.prototype, 'width');
console.log(descriptor);
If the width property were an ordinary property, composed of just a plain value, which was set by the browser via evaling
window.screen.width = 1500
then a proxy or the Object.watch polyfill would work, because you would be able to intercept the assignment to the .width property. This is why you see that, after assigning your own setter to window.screen.width:
Do screen.width = 100; and observe that hello is logged and screen.width is set to 100
It shows you hello - you're invoking the setter that you previously assigned to the screen property of window. In contrast, because the native built-in property is not a plain value which gets assigned to by the browser, your hello does not get logged when the screen changes. The situation is a bit similar to what's going on in this example snippet:
const obj = (() => {
let privateVal = 'propOriginal';
// privateVal changes after 500ms:
setTimeout(() => {
console.log('Changing privateVal to propChanged');
privateVal = 'propChanged';
}, 500);
// Return an object which has a getter, which returns privateProp:
return {
get privateProp() {
return privateVal;
}
};
})();
// At this point, if one only has a reference to `obj`,
// there is no real way to watch for changes to privateVal:
console.log(obj.privateProp);
// Assigning a custom setter to obj.privateProp
// will only result in observing attempted assignments to obj.privateProp
// but will not be able to observe the change to privateVal:
Object.defineProperty(obj, 'privateProp', { set(newVal) {
console.log('Observed change: new value is ' + newVal);
}});
setTimeout(() => {
obj.privateProp = 1000;
}, 2500);
As you can see, the setter function added to obj in the outer scope cannot capture the change to privateVal. This isn't exactly what's happening with window.screen, but it's similar; you do not have access to the underlying code structure that results in changes to the value returned by calling the built-in getter.
The built-in getter function on screen is composed of native code - this means that it's not an ordinary Javascript function. Functions composed of native code are always functions provided by the browser (or whatever environment the code is running in); they often cannot be emulated by any Javascript functions you write yourself. For example, only the window.history object can perform history actions like history.back(); if window.history gets overwritten, and you don't have a saved reference to it or any other history objects, there's no way to write your own function that can do history.back(), because .back() invokes privileged native code that requires an interface between the visible Javascript and the browser engine.
Similarly, the built-in window.screen getter, when called, returns a value not directly observable by plain Javascript. Without polling, the only other way to watch for a change is by listening for the resize event, as covered in the other answer. (This resize event, similar to the underlying value returned by window.screen, is managed by browser internals not observable otherwise.)
If the resize event does not get fired in response to a change in resolution - for example, if the browser window is small enough already that a change is not required for the smaller resolution - then the resolution change does not result in any event being fired, which means there's unfortunately no way to listen for it, aside from polling. (You can try it for yourself, it doesn't look like any other events are triggered on resolution change)
One could probably write (or tweak) a browser engine which listens for a resolution change from the OS and dispatches a Javascript event when found, but doing so would require knowledge far beyond Javascript - feel free to browse Chromium's source code for details, but it's pretty complicated, and probably opaque to one without experience in the languages used.
According to Mozilla, screen resize event is only fired on window object. Therefore, you should add listener to window not to screen :
window.addEventListener('resize', yourfunc)
Tested under Windows 10, it works like a charm with Chrome. Worth to say that the actual screen size computed by the browser use either the screen resolution and the zoom.
For instance, if you're on 1920x1080, zoom 100% :
Passing to 3840x2160, zoom 200% will output a browser window of 1920x1080 and resize seems not triggered
Passing to zoom 150% will output a browser window of 1280x720 and resize is triggered
Nevertheless, there's a pitfall. Screen resolution update will only be detected if it actually triggers a browser window resize. That is not so obvious given the windowed/fullscreen initial state of the browser window and the smaller/bigger screen size evolution.
i am opening a page on the chrome by using F12, and i realize it do have a timer inside the source code, however, the timer started on the page load and it does not store the timer as a variable, what the source code use is
self.setTimeout("OnReload()", 60000);
so if i am intend to using window.clearTimeout(timer);
Since it is not store as a variable, i am not able to using this method to clear the timeout variable, may i ask am i able to clear this timeout???
As you cannot disable the timer, why not just overload the function i.e.
function OnReload() { }
So that it does nowt.
the answer is i key in self.clearTimeOut(0);
and it work
I'm developing a web application in which I've got a video element. This, as I've had good experiences with it, is controlled via the VideoJS scripts.
However, on my app, there are some points in which the video needs to be paused until played by the user (or by a script, it's not relevant), but not after.
What I have
Basically - and the basic information you need - what I have is an array of items, let's say item_array, with objects in it. Each object has an id, a media object with some information of it, including a src, and an indications array - which would be the pause-times objects. Something like that:
var items_array = [
{
id: 1234,
media: {
id: 2345,
src: 'video_source.mp4',
duration: 302.56
},
indications: [
{
id: 1,
time: 65.380
},
{
id: 2,
time: 89.238
},
{
id: 3,
time: 123.765
}
]
}
]
Of course, I've got the video variable, which stores the VideoJS reference and is accessible all along the code as a global variable (maybe it's useful for you to know that).
What I need
Basically, I need to be able to load a video source and tell the script that the video must pause at 1:05.380, 1:29.238 and 2:03.765, for example.
Once paused, the user can play the video again, and it will not be paused (at least, by the script) again until the next time mark is reached.
Then, the user can specify a new video to load, with its new pause-points, so old pause-points should be removed and new ones set.
Also, it would be necessary to perform other actions apart from pausing the video (such as showing a message, etc.). But it's not necessary for you to know any of these - just I have to be able to write some actions to do on every pause-point.
What I've tried
Well, I've been playing with timeupdate. The first problem is that this only fires every now and then, so video.currentTime() == NN.NNN will not always work.
The script video.currentTime() >= NN.NNN is not possible neither, because the user must be able to play further the pause-time (yes, the event can be set off once reached, but if the user skips it on the time line, the video must not be paused).
So, if the pause-time is at 1:05 and the user skips from 0:45 to 1:25, the pause-time set to 1:05 must not be triggered, thus the video must not be paused - so the line written before does not work as I'd like to.
Thank you everyone in advance for your time and help! If you need any further information, please ask me and I will submit it as soon as I can!
Best regards.
Finally, I've come up with a possible solution. Sure it's not the best in performance, but it does what it's meant to.
I write it here and maybe someone will find it useful.
The solution
What I do is to fire a custom event everytime the timeupdate event is triggered, so I can delete all actions attached to this custom event without breaking VideoJS default event actions.
Then, I set a time_range, so I can make the comparison between video.currentTime() with the desired indication.time without being exact, but with some space to happen.
Finally, when it happens, I pause the video (and show the indication, those actions I told you I needed to do) and set a new property of the indication, has_shown to true.
At every custom_timeupdate I check if the video.currentTime() is inside that space of time I told you and if that indication.has_shown is set to false. If so, the process triggers. If not, nothing happens (so if an indication has already been shown it does not pause the video again).
But, as I wanted it to show it again if the user returned to that point, I set a last check so if video.currentTime() is - a little bit - far from that point, the property has_shown is set to false again.
This is done, of course, for every indication we have.
Here you have the code:
var time_range = 0.125
video.on("timeupdate", function(){
video.trigger("indications_timeupdate")
})
function load_indications(indications){
/*
Function actions ...
*/
// If the video is defined, as we are loading them new, we set off old Indication timers
if(video !== undefined) video.off("indications_timeupdate")
$.each(indications, function(i, ind){
video.on("indications_timeupdate", function(){
if(video.currentTime() >= ind.time - time_range && video.currentTime() <= ind.time + time_range && !ind.has_shown){
video.pause()
ind.has_shown = true
}else if(video.currentTime() >= ind.time + time_range || video.currentTime() <= ind.time - time_range){
ind.has_shown = false
}
})
})
}
As I said, maybe it's not the best solution, but it's the best one that I've been able to think of.
Thank you for your time to everyone who has tried to think of an approach for this question!
Best regards.
I have a function running on document load that copies the contents of a select object to other select boxes (to conserve network bandwidth).
The function is taking a few seconds to complete, so I wanted to mask the main div (to give the user the idea that something is happening).
Unfortunately, the mask is not showing up until after the function completes:
// I want the mask to show immediately here, but never gets shown
$('#unassignedPunchResults').mask('Getting results');
$('.employeeList').each(function (i) {
// this is freezing the browser for a few seconds, the masking is not showing
$('#employeeList option').clone().appendTo(this);
});
$('#unassignedPunchResults').unmask();
How can I interrupt the javascript after the mask() call to flush that event and continue, so the user can see the mask while the longer processing (the each()) processes?
Put the rest of the code in a setTimeout(function() { ... }, 0) call.
I've been thinking a while about this.
The first solution is to use the settimeout function.
However it could be a little 'dirty' because you add an arbitrary delay. A more proper logic would be to execute the $('.employeeList').each(function (i)... function after the mask function has benne executed and rendered.
Jquery allows us to do that with the deferred functions like then which xecutes after a deferred condition has been satisfied.
So try with:
// I want the mask to show immediately here, but never gets shown
$('#unassignedPunchResults').mask('Getting results').then(function(){
$('.employeeList').each(function (i) {
// this is freezing the browser for a few seconds, the masking is not showing
$('#employeeList option').clone().appendTo(this);
});
});
In general, using settimeout with an arbitrary number of ms is a solution which works for simple cases, but if you have multiple settimouts in a complex code then you could have synchronizaton problems.
or use a worker, but then you need to discard msie < 10
or break up your calculations in segments that run for less than 500 ms and use setinterval to sread the loading over 5 seconds.
google simulate threading in javascript for code examples.
I have a function called save(), this function gathers up all the inputs on the page, and performs an AJAX call to the server to save the state of the user's work.
save() is currently called when a user clicks the save button, or performs some other action which requires us to have the most current state on the server (generate a document from the page for example).
I am adding in the ability to auto save the user's work every so often. First I would like to prevent an AutoSave and a User generated save from running at the same time. So we have the following code (I am cutting most of the code and this is not a 1:1 but should be enough to get the idea across):
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function save(showMsg)
{
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
//disables the autoSave timer so if we are saving via some other method
//we won't kick off the timer.
disableAutoSave();
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
function endSave()
{
isSaving=false;
//hides popup if it's visible
//Turns auto saving back on so we save x milliseconds after the last save.
enableAutoSave();
}
function endSaveError()
{
alert("Ooops");
endSave();
}
function enableAutoSave()
{
timeoutId=setTimeOut(function(){save(false);},timeoutInterval);
}
function disableAutoSave()
{
cancelTimeOut(timeoutId);
}
My question is if this code is safe? Do the major browsers allow only a single thread to execute at a time?
One thought I had is it would be worse for the user to click save and get no response because we are autosaving (And I know how to modify the code to handle this). Anyone see any other issues here?
JavaScript in browsers is single threaded. You will only ever be in one function at any point in time. Functions will complete before the next one is entered. You can count on this behavior, so if you are in your save() function, you will never enter it again until the current one has finished.
Where this sometimes gets confusing (and yet remains true) is when you have asynchronous server requests (or setTimeouts or setIntervals), because then it feels like your functions are being interleaved. They're not.
In your case, while two save() calls will not overlap each other, your auto-save and user save could occur back-to-back.
If you just want a save to happen at least every x seconds, you can do a setInterval on your save function and forget about it. I don't see a need for the isSaving flag.
I think your code could be simplified a lot:
var intervalTime = 300000;
var intervalId = setInterval("save('my message')", intervalTime);
function save(showMsg)
{
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params, endSave, endSaveError);
// You could even reset your interval now that you know we just saved.
// Of course, you'll need to know it was a successful save.
// Doing this will prevent the user clicking save only to have another
// save bump them in the face right away because an interval comes up.
clearInterval(intervalId);
intervalId = setInterval("save('my message')", intervalTime);
}
function endSave()
{
// no need for this method
alert("I'm done saving!");
}
function endSaveError()
{
alert("Ooops");
endSave();
}
All major browsers only support one javascript thread (unless you use web workers) on a page.
XHR requests can be asynchronous, though. But as long as you disable the ability to save until the current request to save returns, everything should work out just fine.
My only suggestion, is to make sure you indicate to the user somehow when an autosave occurs (disable the save button, etc).
All the major browsers currently single-thread javascript execution (just don't use web workers since a few browsers support this technique!), so this approach is safe.
For a bunch of references, see Is JavaScript Multithreaded?
Looks safe to me. Javascript is single threaded (unless you are using webworkers)
Its not quite on topic but this post by John Resig covers javascript threading and timers:
http://ejohn.org/blog/how-javascript-timers-work/
I think the way you're handling it is best for your situation. By using the flag you're guaranteeing that the asynchronous calls aren't overlapping. I've had to deal with asynchronous calls to the server as well and also used some sort of flag to prevent overlap.
As others have already pointed out JavaScript is single threaded, but asynchronous calls can be tricky if you're expecting things to say the same or not happen during the round trip to the server.
One thing, though, is that I don't think you actually need to disable the auto-save. If the auto-save tries to happen when a user is saving then the save method will simply return and nothing will happen. On the other hand you're needlessly disabling and reenabling the autosave every time autosave is activated. I'd recommend changing to setInterval and then forgetting about it.
Also, I'm a stickler for minimizing global variables. I'd probably refactor your code like this:
var saveWork = (function() {
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function endSave() {
isSaving=false;
//hides popup if it's visible
}
function endSaveError() {
alert("Ooops");
endSave();
}
function _save(showMsg) {
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
return {
save: function(showMsg) { _save(showMsg); },
enableAutoSave: function() {
timeoutId=setInterval(function(){_save(false);},timeoutInterval);
},
disableAutoSave: function() {
cancelTimeOut(timeoutId);
}
};
})();
You don't have to refactor it like that, of course, but like I said, I like to minimize globals. The important thing is that the whole thing should work without disabling and reenabling autosave every time you save.
Edit: Forgot had to create a private save function to be able to reference from enableAutoSave