I have a really strange JavaScript issue that I haven't found any precedent for so hoping someone has seen it.
I'm working on some JavaScript code that resides in SharePoint and I'm trying to use desktop notifications for my Firefox and Chrome users.
I have the following code as just a basic test
$(document).ready(function() {
if ((window.Notification) && (Notification.permission !== "granted")) {
Notification.requestPermission(function(status) {
if (Notification.permission != status) Notification.permission = status;
});
}
if (Notification.permission === "granted") {
var Notification = new Notification("This is a test");
}
});
If I use this code in a custom web page outside of SharePoint it works perfectly fine. If I load it in SharePoint though it's sporadic; some pages it will work just fine but other pages I get an error saying that Notifcation.requestPermission isn't a function. If I do a console.log on the Notification object and Notification.permission I get different results depending on whether I'm on a working page or not working page. On a working page I see Notification as a function with all the correct parameters and Notification.permission comes up as "granted"; the not working pages has Notification as a blank object and Notification.permission is undefined. This happens in both SharePoint 2010 and 2013.
Anyone experienced this before?
This is expected behaviour since you overwrite the Notification object with your own code.
As soon as you've run var Notification = new Notification("This is a test"); you've overwritten the native Notification function code with an instance of the Notification.
You should instead write something like (notice the lower case n)
if (Notification.permission === "granted") {
var notification = new Notification("This is a test");
}
The requestPermission function is declared in the Notification prototype, which is a property of it's constructor function but not the instance, thus you can never call requestPermission on the returned object from new Notification(...).
Related
Im implementing auth using this and am currently showing a loading icon in React when a user clicks the button to sign in and the auth2 account selection/login window shows.
However if a user closes the window, there doesnt seem to be any event fired i.e the signIn() function which returns a promise never resolves, I would have thought google would return an error for this promise if the window is closed. As a result there is no way for me to stop showing the loader icon and reshow the login menu.
I was wondering if anyone had a solution for this?
I try to modifiy my code that call Google OAuth 2.0 window.
You only have to add extra AJAX method that cover what is Google OAuth error result.
gapi.auth2.getAuthInstance().signIn()
Change it to this one,
gapi.auth2.getAuthInstance().signIn().then(function(response){
//If Google OAuth 2 works fine
console.log(response);
}, function(error){
//If Google OAuth 2 occured error
console.log(error);
if(error.error === 'popup_closed_by_user'){
alert('Oh Dude, Why you close authentication user window...!');
}
});
That's it...
For more detail about Google OAuth 2.0 information, you can visit this link.
https://developers.google.com/api-client-library/javascript/samples/samples#authorizing-and-making-authorized-requests
Sample code on JavaScript:
https://github.com/google/google-api-javascript-client/blob/master/samples/authSample.html
Although the API provides a mechanism for detecting when the user clicks the Deny button, there is not a built-in way for detecting that the user abruptly closed the popup window (or exited their web browser, shut down their computer, and so on). The Deny condition is provided in case you want to re-prompt the user with reduced scopes (e.g. you requested "email" but only need profile and will let the user proceed without giving you their email).
If the response from the sign-in callback contains the error, access_denied, it indicates the user clicked the deny button:
function onSignInCallback(authResult) {
if (authResult['error'] && authResult['error'] == 'access_denied') {
// User explicitly denied this application's requested scopes
}
}
You should be able to implement sign-in without detecting whether the window was closed; this is demonstrated in virtually all of the Google+ sample apps. In short, you should avoid using a spinner as you're doing and instead should hide authenticated UI until the user has successfully signed in.
It's not recommended you do this, but to implement detection of the pop-up closing, you could do something like override the global window.open call, then detect in window.unload or poll whether the window was closed without the user authenticating:
var lastOpenedWindow = undefined;
window.open = function (open) {
return function (url, name, features) {
// set name if missing here
name = name || "default_window_name";
lastOpenedWindow = open.call(window, url, name, features);
return lastOpenedWindow;
};
}(window.open);
var intervalHandle = undefined;
function detectClose() {
intervalHandle = setInterval(function(){
if (lastOpenedWindow && lastOpenedWindow.closed) {
// TODO: check user was !authenticated
console.log("Why did the window close without auth?");
window.clearInterval(intervalHandle);
}
}, 500);
}
Note that as I've implemented it, this mechanism is unreliable and subject to race conditions.
I am working with desktop notifications for my web application using the standard Notification API. For the purposes of my initial development, I am using Google Chrome. With Chrome, when a page creates a Notification object, the notification will stay on the desktop forever.
The Notification prototype does have a .close() method which allows for the closing of a notification that has been previously invoked. I figured that this, in conjunction with the setTimeout function would make automatically dismissing notifications after a couple seconds a piece of cake. I even found a guide confirming my idea.
However, it seems that I am unable to get the scope of the notification to work properly with the setTimeout function, and the .close() method does not get called properly for each created notification.
Here is what I have tried (I used some code found in another answer as a starting point):
Button:
<button onclick="notifyMe()">
Notify me!
</button>
JavaScript:
<script>
// request permission on page load
document.addEventListener('DOMContentLoaded', function () {
if (Notification.permission !== "granted")
Notification.requestPermission();
});
function notifyMe() {
if (!Notification) {
//alert('Desktop notifications not available in your browser. Try Chromium.');
return;
}
if (Notification.permission !== "granted")
Notification.requestPermission();
else {
var notification = new Notification('Notification');
notification.onclick = function () {
window.focus();
};
setTimeout(notification.close, 2000);
// Result: Uncaught TypeError: Illegal invocation
// also tried.....
// setTimeout(notification.close(), 2000);
// Result: notification stays open forever
// setTimeout('notification.close', 2000);
// Result: ReferenceError: notification is not defined
}
}
</script>
I would appreciate it if anyone who has experience with this could help me.
When I wrap that into a function() {} it works:
setTimeout(function() { notification.close() }, 2000);
See this fiddle: https://jsfiddle.net/drnz12n8/2/
I am trying to create a notification in Chrome.
I have write this simple code but there is notification shown in CHrome whereas checkPermission() return well 0.
I do the same thing than this website (example), which works fine in my Chrome browser.
if (window.webkitNotifications.checkPermission() == 0) {
window.webkitNotifications.createNotification("icon.png", "title", "text").show();
} else {
window.webkitNotifications.requestPermission();
}
Where is the problem ?
[EDIT : Problem fixed]
In fact, i allows to show notification from all website in Chrome settings, and now, it works fine !
Request permission only work on a user gesture (see below question, and quote from the docs).
In short you need to register a click event or something similar then request permission.
document.querySelector('#show_button').addEventListener('click', function() {
if (window.webkitNotifications.checkPermission() == 0) { // 0 is PERMISSION_ALLOWED
var notification = window.webkitNotifications.createNotification(
'icon.png', 'Notification Title', 'Notification content...');
notification.show();
} else {
window.webkitNotifications.requestPermission();
}
}, false);
Here's a jsfiddle
Webkit notifications requestPermission function doesn't work
requestPermission Requests that the user agent ask the user for
permission to show notifications from scripts. This method should only
be called while handling a user gesture; in other circumstances it
will have no effect. This method is asynchronous. The function
provided in callback will be invoked when the user has responded to
the permission request. If the current permission level is
PERMISSION_DENIED, the user agent may take no action in response to
requestPermission.
I'm wrapping a web app in a Windows Store app shell using a x-ms-webview. This works fine, but I have one problem. I use PayPal and since PayPal doesn't allow to be iframed I need to open PayPal in a new browser window.
On regular browsers this isn't a problem. The window open and when the user returns from PayPal I can a callback on "opener" and update the users account.
But when doing this in a Windows Store app the window.open triggers IE to launch. The problem is to return to my app and let it know that the user finished the transaction.
My first idea was just to use a URI activation. This kind of works, but I having trouble knowing if the PayPal page was launch from a regular browser or an app. I also think it is confusing for the user to be taken to another app to make the purchase.
I would prefer to have the window open in my app, but I'm not sure how I would open open a new x-ms-webview as a modal window overlapping existing webview.
What is the best way to communicate from the current web view and the app?
Can I use postMessage to send messages between the app and the x-ms-webview even though the src of the web view is a http hosted site?
Thank you for your help.
I found a solution to this.
First, you will need to use a https url for the embedded site. The reason for this is that the solution include postMessage and invokeScriptAsync.
First, my markup in my app looks something like this to have one webview for the app and one web view for the PayPal popup.
<x-ms-webview id="webview" src="https://myapp"></x-ms-webview>
<div id="paypalContainer">
<div class="paypal-header"><button id="paypalClose" type="reset">Close</button></div>
<div class="paypal-body"><x-ms-webview id="paypalWebView" src="about:blank"></x-ms-webview></div>
</div>
Then, when the web app is ready to use PayPal, I use window.external.notify to send a message to the Windows Store app.
if (window.external && 'notify' in window.external) {
window.external.notify(JSON.stringify({ action: 'paypal' }));
}
The windows store app listens for Script Notify events and displays the paypalWebView.
webview.addEventListener("MSWebViewScriptNotify", scriptNotify);
function scriptNotify(e) {
var data = JSON.parse(e.value);
if (data.action === "paypal") {
var loading = document.getElementById("loading-indicator");
var container = document.getElementById("paypalContainer");
var paypalWebView = document.getElementById("paypalWebView");
var paypalClose = document.getElementById("paypalClose");
if (paypalWebView.src === "about:blank") {
paypalWebView.addEventListener('MSWebViewNavigationCompleted', function (e) {
loading.classList.remove('loading');
var successUrl = '/paypal/success';
if (event.target.src.indexOf(successUrl) !== -1) {
var operation = webview.invokeScriptAsync("updateUser");
operation.oncomplete = function () {
(new Windows.UI.Popups.MessageDialog("Your account is refreshed", "")).showAsync().done();
};
operation.start();
}
});
paypalWebView.addEventListener('MSWebViewNavigationStarting', function (e) {
console.log("Started loading");
loading.classList.add('loading');
});
paypalClose.addEventListener('click', function () {
container.classList.remove("visible");
});
}
paypalWebView.src = "https://myapp/paypal/";
container.classList.add("visible");
}
}
So, basically, when the script notify event fires, I parse the sent json string to an object and check what kind of action it is. If it's the first time I run this I setup some naviation event handlers that check if the web view reach the Success page. If we have, I use incokeScriptAsync to let the web app know that we're done so it can refresh the user account the new payment.
I think you can use a similar solution for authentication and just check your your return URL after authenticating.
Hope this helps!
Okay. So i have heard plenty about building apps using phonegap and html5. the app works fine but it access a web service to populate the div for display. The service runs well what i want to implement is a caching mechanism for storing the last interface from the service using local-storage while a fresh request is going on in the background.
This is my javascript code. Please what am i doing wrong.
P.S am a html5/js intermediate user. However, i user php really well.
function loadHome()
{
$("#post_display").html(waitText);
$.get(api_url',{ app_key: my_app_key } ,
function(data){
if(('localStorage' in window) && window['localStorage'] !== null)
{
localStorage.setItem('data_home',data); alert(localStorage.getItem(data_home));
}
$("#post_display").html(data) }
);
}
so far, the alert works and shows me the data stored. Now how do i display the data when the user returns to the page. what conditional logic do i use?
sample
if(localstorage.data_home){ //display store data here }
else{//run the ajax call to the api service}
i dont know how to go about this. Help.
just check before you call ajax. It's just a sample, you need to refact your source code.
function loadHome()
{
$("#post_display").html(waitText);
if(('localStorage' in window) && window['localStorage'] !== null && localStorage.getItem('data_home')){
$("#post_display").html(localStorage.getItem('data_home'));
return;
}
$.get(api_url',{ app_key: my_app_key } ,
function(data){
if(('localStorage' in window) && window['localStorage'] !== null)
{
localStorage.setItem('data_home',data); alert(localStorage.getItem(data_home));
}
$("#post_display").html(data) }
);
}