IE 11 window.open() with javascript script loading race condition issue - javascript

Goal:
I have a ReactJs web app called "ImageGallery" that's currently integrated in our business suite system(which is a huge system contains older technologies, mainly web forms). In our system, we need to add a button to launch a browser window to spin the app.
ImageGallery app rely on the current system to pass data to it to show, which means, when the ImageGallery app loads, it calls a function on the business suite system side like
window.parent.loadData()
Since the data is passed in by JavaScript, there is no ajax call.
Code:
function showImage() {
var imageGalleryWindow = window.open('/ImageGallery', '_blank', 'height=' + window.screen.availHeight + ',width=' + window.screen.availWidth + ',scrollbars=yes,menubar=no,resizable=yes,toolbar=no,location=no,status=no');
imageGalleryWindow.loadData= loadData;
}
loadData() function is available on the current JavaScript file and will be passed to the new window so it can be used by the ReactJS app.
Problem:
The app works perfectly with Chrome/Firefox/Edge, but not IE 11. IE 11 sometimes loads, sometimes not. After taking a closer look, I found there's a race condition.
Basically whenever the app failed, loadData() function has not been passed to the newly created window. Again, that's only for IE11, all other browsers seems fine.
What I've tried:
I tried the following:
1> Tried to put a wait function in ImageGallery app like this:
static resolveDocuments(){
if(typeof window.parent.loadData === 'function'){
// do the work to show image
} else {
setTimeout(this.resolveDocuments, 2000);
}
}
I am using redux thunk, I didn't get any luck in getting this to work.
2> put imageGalleryWindow.loadData= loadData; into window.onload(), bad idea but tried anyways
3> put imageGalleryWindow.loadData= loadData; into imageViewerWindow.document.addEventListener("DOMContentLoaded", function (event) {} still bad idea but tried anyways
4> created a new ImageGallery.html, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck
5> created a webform page ImageGallery.aspx, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck.
Thanks for taking time to finish reading this problem, and please let me know if you have an idea I can try.
Thanks so much!
Ray

The solution I end up having is html 5 local storage:
Before calling window.open() to populate the new window, create a entry in html 5 local storage with all the data I need to pass in.
When the image gallery app loads, check if local storage items exists, if so, pull the data. The data will always be there, since it sets before new window generated, and since both pages are from the same domain, they could both access the same local storage.
To avoid security issues, after image gallery app read data from the local storage, I keep a copy of the data within the app, then delete the local storage entry.
Hope this can be helpful to other people.

Related

How to make sure the browser read the latest static files immediately

I serve the static webapp on aws s3 that distributed through CDN (aws cloudfront). The files are ES6 based build version with rollup. For short info, except index.html rollup will generate new hash files every build the webapp. So the files are always unique every update, except index.html. Then on the aws cloudfront I put the index.html into invalidation cache list.
Well then, I will expect users always request the latest version of the webapp with that approach. Yes it works, but with a little note!
So once there is new updates, the browser is still loading the old index.html file on the first time. I have to refresh page to push the browser get the latest index.html. It's not good for the end users. They doen't want to know about refreshing, most will not know right?
One last experiment, I added small script inside on index.html to perform version validation like so :
<script>
fetch('/version.json').then(r => {
if (r.status == 200) {
return r.json()
} else {
alert("Found server updated, let us resync the contents!")
location.reload(true)
}
}).then(j => {
if (window.localStorage.getItem("app-version")) {
if (j.version != window.localStorage.getItem("app-version")) {
alert("Found server updated, let us sync the contents!")
window.localStorage.setItem("app-version", j.version)
location.reload(true)
}
} else {
window.localStorage.setItem("app-version", j.version)
}
})
</script>
That script worked as expected but wondering whether I will have better solution out there? Kindly to have another idea, please?
Expected behavior
Browser have knowledge the latest index.html immediately without any refresh/reload page from end users.
Thank you
Just use a timestamp in the URL to force the browser to get the latest version.
(()=>{
// Get the current timestamp
var now = new Date().getTime();
// Check for a ts parameter in the url (index.html?ts=2345234523)
const urlParams = new URLSearchParams(location.search);
var ts = urlParams.get('ts');
// If there is no timestamp parameter,
// or the timestamp parameter is older than 1 minute
// redirect the page to the latest version
if(!ts || now - ts > 60000){
location.href = `index.html?ts=${now}`;
}
})();
You'll want to put that near the top so you don't have a lot of things loading before the redirect.
I would take advantage of a service-worker to make it easier and more efficient to load and cache a cohesive version of the app.
The service worker has a built in mechanism for checking whether the app has been updated, which is does each time the app is launched.
What it won't do is trigger that check for you while the app is running. While you could poll regularly to check for updates, it's more efficient for both your server and users to have them subscribe for updates which you can do using something like Firebase RTDB (Real Time Database):
When you publish a new version and want to immediately force all running instances to update, you change the version or timestamp that they are subscribing to and have that trigger the service-worker update check, which then refreshes and reloads the app.
There are lots of available patterns for prompting users about any update in case they are in the middle of completing a form etc...

When moving to new URL with Chrome console, is there a way to keep variables alive?

I'm totally new to JavaScript either Chrome console.
My search of the literature was not enough to answer the question.
But I found it is hard to find any previous questions which tell me "How to prevent variable flushing(?) when you move to new URL in Chrome console".
For example, Let's say right now I'm in Google Starting Page ("https://www.google.com/")
Then I open Chrome console, make integer variable and function like this...
a = 10
function myFunc(){
console.log("Hello")
}
After that, I move to Yahoo homepage with this code
this.document.location = "https://www.yahoo.com/"
Then, as you can expect, my previously defined variable and function are GONE.
When I try to call my previous variable a in Yahoo,
Uncaught ReferenceError: a is not defined
at <anonymous>:1:1
Until now I'm stuck on this problem.
I wonder how to tell browser to keep variables and functions alive whenever moving to new URL?
All you do in the console, you do in the context of the current page (its window global object and local scope). If you change the page, this context is gone.
But you can quickly re-initialize the state if you use a scratchpad script in the "Sources" tab (and "Snippets" subtab) of the developer tools. Just create a new script and press Ctrl+S. Then you can select "Run" in the context menu of this script each time the page is changed and the state will be re-created:
No you can't, however If you wanna "save" some values you can send them to your server (as ajax request with json) or put them as your link parameter e.g. https://your.server.com/save?a=valueOfA (of course that server must support this parameter and return response which will set proper values to proper variables)
you can use localStorge() to save data on client browser it wont work for function tho just wanted to mention it

Error: Could not complete the operation due to error 8150002e

I am working on a Outlook VSTO add-in, in which I am using 'System.Windows.Forms.WebBrowser' to display the add-in functionality.
One functional requirement is to have oauth connection to cloud accounts (like OneDrive, Dropbox). When user clicks a button (e.g. 'Connect OneDrive'), we call 'window.open' in JavaScript code (ES6) with the oauth-url.
The issue which I am facing is, if user doesn't enter any credentials and close the window, and then again try to connect the cloud account (by clicking the 'Connect OneDrive'), I am getting an exception (Error: Could not complete the operation due to error 8150002e.).
I couldn't find any information about the error code '8150002e' on web.
This exception is not always present but comes around 50% of the times.
Any help would be appreciated in this.
what I have tried:
changing the windowName param every time window.open is called
having global var for window Object.
Using _blank parameter to open a new window every time.
After 5-6 times, the error comes up, after 5-6 times error goes away and auth window start coming up like before.
Opening a simple static HTML page in 'window.open' to verify if the issue has something to do with HTML page. The above issue is still present.
Resetting the System.Windows.Forms.WebBrowser programmatically.
Removing the cookies.
Instead of calling window.open from JavaScript code, we call VSTO code to open the browser window, the error is still there.
Edit: Created a minimal viable example at
https://github.com/vinay-x/SampleAddin
Code related to the issue:
SDXOLForm1.cs (navigates the browser to SamplePage.HTML hosted on localhost:8001)
SamplePage.HTML (contains a button, which calls window.open function).
The sample application has a windows form which contains a webBrowser control, which navigates to a simple HTML page which contains a button.
Had to deal with some IE11 stuff and I ran across this question. The solution I found to fix this issue is to set the window variable to null prior to calling window.open.
So for your example you have this:
function myFunction() {
window.open("https://www.w3schools.com", 'BackfliptOAuth', "width=800,height=800,center=true,useContentSize=true");
}
I modified it to this:
var win = null;
function myFunction() {
win = null;
win = window.open("https://www.w3schools.com", 'BackfliptOAuth', "width=800,height=800,center=true,useContentSize=true");
}

How to erase, clear, wipe HTML 5 localstorage for a web app saved to home screen on an iPad?

There is a fine summary of how localstorage works in iOS 6 here:
http://blog.nsbasic.com/2012/09/important-io6-and-data-storage/
Many seem to be concerned by the fact that safari and web apps do not share localstorage etc.. However, I can't seem to find the answer to the question in the title anywhere:
My situation: I am developing a HTML5 web app with web-app-capable and want it to be able to run from the home screen. Everything seems to work fine, but for development, I need to be able to clear the cache once in a while. I have tried everything I could think of (and any thinkable combination and sequence of them...):
Clear Safari Cache
Delete app from home and reload it
Reload the web page over and over before saving it to home
Clearing the cache (localStorage.clear()) from within the app
What are your ideas?
You can use this function to clear the localStorage.
localStorage.clear()
You can add counter to be stored in localStorage and do something like this.
onload:
localStorage.reloadCount = localStorage.reloadCount || 0
//make sure its not undefined
localStorage.reloadCount = localStorage.reloadCount*1;
// it stores in string values
if(localStorage.reloadCount>4)
localStorage.clear()

Google Gadget canvas view unable to talk to gadgets.views or .params until refresh?

I've been working on a gadget that will run in iGoogle. In fact, it's finished, aside from a nasty bug that we're having a nightmare of a time hunting down.
Note that the code snippets are in CoffeeScript; it's compiled to JS before we host it up for Google. We're also using Backbone.js for our code, but that shouldn't make much difference as all of our Backbone-specific code (initialize methods and so forth) work just fine; I've been able to isolate the problem parts of the code outside of our Backbone stuff.
The gadget saves a list of preferred options to Google's gadgets.prefs object, as a pipe-delimited string (55|34|101).
prefs = new gadgets.Prefs()
prefs.set('paramname','55|34|101')
This save happens just fine, and any subsequent call to get that pref works just fine. They're even readable by a prefs.get call in the home view. It's when switching from the home view to the canvas view that the gadget isn't able to get those preferences (from the canvas view).
prefs = new gadgets.Prefs()
prefs.get('paramname')
Only once you do a Refresh on the whole canvas view page will the gadget retrieve those preferred options.
Now get this: we've tried passing these preferences as params between the home and the canvas view as well.
(home view)
params =
'paramname': '55|34|101'
gadgets.views.requestNavigateTo('canvas', params)
(canvas view)
#params = gadgets.views.getParams()
This successfully sends us to canvas, but after switching home to canvas and trying to talk to gadgets.views.getParams(), an empty object is returned. Only once a Refresh is done does the gadget process that same call, find the params, and use them.
In both views, the last piece of code run is…
gadgets.util.registerOnLoadHandler init
Which calls our init() function…
init = ->
#params = gadgets.views.getParams()
…which returns an empty object, unless we refresh the page.
Any idea why this is happening? Am I not "kickstarting" the gadgets.* stuff properly? It's almost like I'm talking to a gadgets.* object that's not "bound" to my particular gadget, until I refresh the page (which then forces it to bind to the canvas view). Please help!
As it seems, it's an issue with Google's new AJAX loading technology used in switching between home/canvas views. We contacted Google's iGoogle/OpenSocial team and that's the response we got. They were able to reproduce the bug with a gadget of their own. Until they fix it, accessing iGoogle with http://google.com/ig?aig=0 should band-aid the issue.

Categories

Resources