I've been trying to redirect users to the "action" part of Web push coming from the backend (PHP).
return (new WebPushMessage)
->title('Title')
->icon('icon.png')
->body('Body Msg')
->action('Open Notification', 'open_notification')
->data(['id' => $notification->id,'url'=>'http://somewhere']);
Default service workers use:
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow('/');
}));
});
From https://developer.mozilla.org/en-US/docs/Web/Events/notificationclick
Angular 5 uses
import {SwPush, SwUpdate} from '#angular/service-worker';
My question then is how to interpret this in the front end (Angular 5) using #angular/service-worker
Got a response from u-ryo on Github
There is a workaround.
Add the codes below to ngsw-worker.js around the line
this.scope.addEventListener('push', (event) => this.onPush(event));(Line 1775).
this.scope.addEventListener('notificationclick', (event) => {
console.log('[Service Worker] Notification click Received. event', event);
event.notification.close();
if (clients.openWindow && event.notification.data.url) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});
Then you can specify the URL in the "notification.data.url".
https://github.com/angular/angular/issues/20956#issuecomment-374133852
If you want to have actions, and each action with a different url, here is my fix.
this.scope.addEventListener('notificationclick', (event) => {
event.notification.close();
var payload = event.notification.data;
var url = payload.url;
if (event.action && payload.actions && payload.actions.length) {
var actions = payload.actions.filter(x => x.action == event.action);
if (actions.length && actions[0].url) {
url = actions[0].url;
}
}
if (clients.openWindow && url) {
event.waitUtil(clients.openWindow(url));
}
});
Related
I have a vue function call which is triggered when selecting a radio button but it seems that my code inside my $nextTick is running before my previous line of code is finished. I don't want to use setTimout as I don't know how fast the user connection speed is.
findOrderer() {
axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers)
...OTHER_CODE
}
rbSelected(value) {
this.findOrderer();
this.newOrderList = [];
this.$nextTick(() => {
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.$nextTick(() => {
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
})
})
}
Looking at the console log the 'FINE_ORDERER' console.log is inside the 'findOrderer' function call so I would have expected this to be on top or am I miss using the $nextTick
That's expected, since findOrderer() contains asynchronous code. An easy way is to simply return the promise from the method, and then await it instead of waiting for next tick:
findOrderer() {
return axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers);
});
},
rbSelected: async function(value) {
// Wait for async operation to complete first!
await this.findOrderer();
this.newOrderList = [];
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
}
I have a guard for my routes which does work in all aspects apart from when I wish to check a permissions object related to that page prior to continuing onto the url.
I have separated out the logic for the permissions check into a function and console logging it out all elements are working, in that it finds the right object in the array, finds the right key and value, and allows or blocks accordingly.
My problem though is that when I go through this route hitting this function it loops endlessly and crashes the browser. I think my overarching logic is fine but have I screwed up its implementation somewhere?
function guard (to, from, next) {
var loggedin_state = store.state.user.auth.loggedin // Boolean
var user = store.state.user.user // Array
var token = store.state.user.auth.token // String
var entryUrl // String
if(entryUrl == null || entryUrl == undefined){
entryUrl = to.path
}
if(loggedin_state == true) {
// Is the user profile blank
if(user == null) {
this.$store.dispatch('user/get_user_information', null)
}
// If they tried a route before logging in that would have been stored
if(entryUrl) {
// Store the url before wiping it
let url = entryUrl;
// Wipe the entry url variable
entryUrl = null;
// Carry on to permission checking function
return go_to_url(url);
} else {
// Go to stored url
return next(to.path)
}
} else {
// Is there a token assigned? If so they are approved and just need the profile information
if(token !== null) {
loggedin_state = true
this.$store.dispatch('user/get_user_information', null)
return go_to_url(to.path);
} else {
// Store entry url before redirect for use after login
entryUrl = to.path
// Re-route to login page
return next("/login");
}
}
function go_to_url(url) {
// Find matching object in user.permissions based upon url
var view_permissions = [
{ "area": "all", "read": 1, "create": 0, "edit": 0, "delete": 0 },
{ "area": "dashboard", "read": 1, "create": 0, "edit": 0, "delete": 0 }
];
// var view_permissions = store.state.user.permissions
var view_permission = view_permissions.find(view => view.area === to.name);
if(view_permission.read == 1) {
// Go to url
next(url);
} else {
// Re-route to somewhere
}
};
};
My problem was in passing a value to the next() function call. By removing that out it worked alright:
function guard (to, from, next) {
console.log('To:')
console.log(to)
console.log('From:')
console.log(from)
console.log('EntryUrl: ' + entryUrl)
// 1 - If no entry path was provided then set to
if(entryUrl == null || entryUrl == undefined){
entryUrl = to.path
console.log('EntryUrl: ' + entryUrl)
}
// 2 - Check if the user is marked as being logged in
var loggedin_state = store.state.user.auth.loggedin
if(loggedin_state == undefined) {
store.commit('user/set_delete_session', null)
return next("/login");
}
// 3 - If they are marked as logged in continue
var user = store.state.user.user
var token = store.state.user.auth.token
if(loggedin_state == true) {
// If the user isn't authorised with a token then send them to the log in page
if(token == null) {
store.commit('user/set_delete_session', null)
return next("/login");
}
// If they've got a token but no user profile data acquire it
if(user == null) {
UserApi.get_user_information(response.data.token)
.then(response => {
store.commit('user/set_user', response.data)
})
}
// If they tried a route before logging in that would have been stored
if(entryUrl) {
console.log('Go to saved URL')
// Store the url before wiping it
let url = entryUrl;
// Wipe the entry url variable
entryUrl = null;
// Go to stored url
return go_to_url(url);
} else {
console.log('Go to pointed url')
// Carry on to permission checking function
return go_to_url(to.path);
}
} else {
// The user is not logged in. Store the URL they were trying to visit and redirect them to the login page
entryUrl = to.path
console.log('EntryUrl: ' + entryUrl)
return next("/login");
}
function go_to_url(url) {
console.log(url)
// 1 - Grab the user permissions from the user profile
var permissions_array = null
if(user !== null) {
permissions_array = user.permissions
}
console.log(permissions_array)
// 2 - Check and route
if(permissions_array !== null) {
// Find the relevant permission object based upon the route name and the area key
var view_permissions = permissions_array.find(view => view.area === to.name);
console.log(view_permissions)
// If a permission object was found check its status, if no object found assume it is okay to view
if(view_permissions !== undefined) {
// If set to 1 the user can view this route, else reroute to a permissions denied page
if(view_permissions.read == 1) {
// Go to url
console.log('GUARD - PROCEED')
console.log(to.name)
next();
} else {
console.log('GUARD - BLOCKED')
return next("/permission-denied");
}
} else {
return next()
}
}
};
};
I need to modify the Kurento group call example from Link
to send only audio if one participant has no camera. Right now only audio is received when a camera is used. When only a microphone is available I receive a DeviceMediaError.
I managed to filter whether a camera device is connected or not and then send only audio, but this doesn't work. Maybe the participant should've an audio tag instead of a video tag?
EDIT: It's only working on Firefox and not in Chrome. Any ideas?
in file - https://github.com/Kurento/kurento-tutorial-java/blob/master/kurento-group-call/src/main/java/org/kurento/tutorial/groupcall/UserSession.java.
change following line -
sender.getOutgoingWebRtcPeer().connect(incoming, MediaType.AUDIO);
and set offer media constraints to video:false in browser js file.
updated code -
let constraints = {
audio: true,
video: false
};
let localParticipant = new Participant(sessionId);
participants[sessionId] = localParticipant;
localVideo = document.getElementById('local_video');
let video = localVideo;
let options = {
localVideo: video,
mediaConstraints: constraints,
onicecandidate: localParticipant.onIceCandidate.bind(localParticipant),
configuration : { iceServers : [
{"url":"stun:74.125.200.127:19302"},
] }
};
localParticipant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function(error) {
if (error) {
return console.error(error);
}
localVideoCurrentId = sessionId;
localVideo = document.getElementById('local_video');
localVideo.src = localParticipant.rtcPeer.localVideo.src;
localVideo.muted = true;
this.generateOffer(localParticipant.offerToReceiveVideo.bind(localParticipant));
});
server.js code
function join(socket, room, callback) {
let userSession = userRegister.getById(socket.id);
userSession.setRoomName(room.name);
room.pipeline.create('WebRtcEndpoint', {mediaProfile : 'WEBM_AUDIO_ONLY'}, (error, outgoingMedia) => {
if (error) {
console.error('no participant in room');
if (Object.keys(room.participants).length === 0) {
room.pipeline.release();
}
return callback(error);
}
// else
outgoingMedia.setMaxAudioRecvBandwidth(100);
add media profile parameter on server side while joining room.
function getEndpointForUser(userSession, sender, callback) {
if (userSession.id === sender.id) {
return callback(null, userSession.outgoingMedia);
}
let incoming = userSession.incomingMedia[sender.id];
if (incoming == null) {
console.log(`user : ${userSession.id} create endpoint to receive video from : ${sender.id}`);
getRoom(userSession.roomName, (error, room) => {
if (error) {
return callback(error);
}
room.pipeline.create('WebRtcEndpoint', {mediaProfile : 'WEBM_AUDIO_ONLY'}, (error, incomingMedia) => {
if (error) {
if (Object.keys(room.participants).length === 0) {
room.pipeline.release();
}
return callback(error);
}
console.log(`user: ${userSession.id} successfully create pipeline`);
incomingMedia.setMaxAudioRecvBandwidth(0);
incomingMedia.getMaxAudioRecvBandwidth(0);
add media profile parameter when accepting call.
hope this helps.
I can't call any external functions inside an axios promise. My error is
[Uncaught (in promise) TypeError: this.showNotification is not a function]
showNotification is an external function inside mixin but i can't call it in userCompInfo function
var mixin = {
methods: {
//function to get all user info
userInfo:function(){
//alert(localStorage.getItem("sessionKey"))
if(localStorage.getItem("sessionKey")==null)
session = "null";
else
session= localStorage.getItem("sessionKey");
return axios.post("/api/user/info",{
user_id:localStorage.getItem("userId"),
session_key:session
});
},
//function to get all competition info
compInfo:function()
{ //calling API competition info to get the avaliable competition for now
return axios.post("/api/competition/info",{
lang:source.lang
});
},
userCompInfo:function()
{
axios.all([this.userInfo(),this.compInfo()])
.then(axios.spread(function (user, comp){
if(comp.data.result.errorcode == 0)
{
source.competition = comp.data.competiton_detail;
}
//set the user id in local storage
localStorage.setItem("userId",user.data.user_info.user_id);
//check on session key to save it in local storage
if(user.data.user_info.session_key != "") // for testing
localStorage.setItem("sessionKey",user.data.user_info.session_key);
// get user data successfuly
if(user.data.result.errorcode == 0)
{ //set response data in user
source.user = user.data.user_info;
//to set this user status
this.renderUserInfo();
///////////
//source.competition.is_active=1;
//check on user status to show notification
if(source.user.vip && source.user.suspended)
{
this.showNotification(context.suspended_vip.title+" "+source.user.msisdn, context.suspended_vip.body,["btn_notifi_ok"]);
}
//check if this valid competition is active or not to show notification according to it and to user status
if(source.competition.is_active==1)
{
if(source.user.registered && newUser==1)
{
this.showNotification(context.registered_firstTime.title, context.registered_firstTime.body, ["btn_notifi_start_play"]);
}
else if(source.user.free && source.user.newFreeUser)
{
this.showNotification(context.freeUser_firstTime.title, context.freeUser_firstTime.body, ["btn_notifi_start_play"]);
}
}
}
}));
},
// function to show notification modal
showNotification:function(title, body, buttons)
{
//to hide all modal buttons first
$("#notification").find(".btn").hide();
//set the modal title
$("#notification_title").html(title);
//set the modal body
$("#notification_body").html(body);
//for on buttons array to show all buttons that we want
for(i=0;i<buttons.length;i++)
{
$("#notification").find("#"+buttons[i]).show();
}
//to display modal
$("#notification").modal("show");
},
//function to show the current user statue VIP, registered,free or suspended
renderUserInfo:function()
{ // intialize all flags of user status to false
source.user.vip = false;
source.user.registered = false;
source.user.free = false;
//check on user mode and set the according flag to it to true
if(source.user.sub_mode == "vip")
{
source.user.vip = true;
}
else if(source.user.sub_mode == "sub")
{
source.user.registered = true;
}
else
{
source.user.free = true;
}
},}}
This is a very common mistake. this in userCompInfo doesn't refer to the Vue because of they way you have written the callback.
In your code, make this change.
userCompInfo:function()
{
axios.all([this.userInfo(),this.compInfo()])
.then(axios.spread(function (user, comp){
// a bunch of code...
}.bind(this)));
},
Or
userCompInfo:function()
{
let self = this;
axios.all([this.userInfo(),this.compInfo()])
.then(axios.spread(function (user, comp){
// a bunch of code...
self.showNotification(...)
// some more code...
}));
},
See How to access the correct this inside a callback?
I'm having a problem using the adal.js library without Angular. (I'm using Vue.js.)
I have an authentication context instance, which is constructed with the following options (exact values have been changed to protect the innocent):
let config = {
tenant: '<tenant id>',
clientId: '<client id>',
redirectUri: 'http://myapplication.com/index.html',
// popUp: true,
cacheLocation: 'localStorage'
}
On my login page, I call authContext.login(), which redirects me first to https://login.microsoftonline.com/, where I log into AAD. Upon successful login, another redirect takes me back to my application, at the URI I've configured above, along with an id_token parameter in the URL. However, no token or other properties are stored by the library in local storage, just a few properties that are the result of the configuration.
On successful login, All I've got in localStorage is
{
adal.access.token.key: "",
adal.error: ""
adal.error.description: ""
adal.expiration.key: "0"
adal.idtoken: ""
adal.login.error: ""
adal.login.request: "http://myapplication.com/#/login"
adal.nonce.idtoken: "<a non-empty string>"
adal.session.state: ""
adal.state.login: "<a non-empty string>"
adal.token.keys: ""
adal.username: ""
}
So, as far as AAD is concerned, I've successfully authenticated, but the library itself seems to have no notion of what user is logged in, what tokens are associated with them, when the token expires, etc. Any advice on how to proceed would be most appreciated. Thank you in advance for reading.
Active Directory Authentication Library for JavaScript (ADAL JS) helps you to use Azure AD for handling authentication in your single page applications. This library is optimized for working together with AngularJS.
It will not save the tokens into the cache unless we code it. You can check the relative code from adal-angular.js. Here is a piece of code for your reference:
The saveTokenFromHash method will also save the tokens into cache and this function will execute after the page redirect back to the Angular app.
adal.js:
AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
this._logstatus('State status:' + requestInfo.stateMatch);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
// Record error
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)) {
this._logstatus('Error :' + requestInfo.parameters.error);
this._logstatus('Error description:' + requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, requestInfo.parameters.error);
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
this._loginInProgress = false;
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, requestInfo.parameters.errorDescription);
} else {
this._renewActive = false;
}
} else {
// It must verify the state from redirect
if (requestInfo.stateMatch) {
// record tokens to storage if exists
this._logstatus('State is right');
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.SESSION_STATE)) {
this._saveItem(this.CONSTANTS.STORAGE.SESSION_STATE, requestInfo.parameters[this.CONSTANTS.SESSION_STATE]);
}
var keys, resource;
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)) {
this._logstatus('Fragment has access token');
// default resource
this._renewActive = false;
resource = this.config.loginResource;
if (!this._hasResource(resource)) {
keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
}
if (requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN) {
resource = this._getResourceFromState(requestInfo.stateResponse);
}
// save token with related resource
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource, requestInfo.parameters[this.CONSTANTS.ACCESS_TOKEN]);
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource, this._expiresIn(requestInfo.parameters[this.CONSTANTS.EXPIRES_IN]));
}
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ID_TOKEN)) {
this._loginInProgress = false;
this._user = this._createUser(requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
if (this._user && this._user.profile) {
if (this._user.profile.nonce !== this._getItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN)) {
this._user = null;
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Nonce is not same as ' + this._idTokenNonce);
} else {
this._saveItem(this.CONSTANTS.STORAGE.IDTOKEN, requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
// Save idtoken as access token for app itself
resource = this.config.clientId;
if (!this._hasResource(resource)) {
keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
}
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource, requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource, this._user.profile.exp);
}
}
}
} else {
this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Invalid_state');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Invalid_state');
if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'State is not same as ' + requestInfo.stateResponse);
}
}
}
};
And this function will be called in this.$get like below:
// special function that exposes methods in Angular controller
// $rootScope, $window, $q, $location, $timeout are injected by Angular
this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', function ($rootScope, $window, $q, $location, $timeout) {
var locationChangeHandler = function () {
var hash = $window.location.hash;
if (_adal.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = _adal.getRequestInfo(hash);
_adal.saveTokenFromHash(requestInfo);
$window.location.hash = '';
if (requestInfo.requestType !== _adal.REQUEST_TYPE.LOGIN) {
_adal.callback = $window.parent.AuthenticationContext().callback;
}
// Return to callback if it is send from iframe
if (requestInfo.stateMatch) {
if (typeof _adal.callback === 'function') {
// Call within the same context without full page redirect keeps the callback
if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
// Idtoken or Accestoken can be renewed
if (requestInfo.parameters['access_token']) {
_adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']);
return;
} else if (requestInfo.parameters['id_token']) {
_adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']);
return;
}
}
} else {
// normal full login redirect happened on the page
updateDataFromCache(_adal.config.loginResource);
if (_oauthData.userName) {
//IDtoken is added as token for the app
$timeout(function () {
updateDataFromCache(_adal.config.loginResource);
$rootScope.userInfo = _oauthData;
// redirect to login requested page
var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE);
if (loginStartPage) {
$location.path(loginStartPage);
}
}, 1);
$rootScope.$broadcast('adal:loginSuccess');
} else {
$rootScope.$broadcast('adal:loginFailure', _adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION));
}
}
}
} else {
// No callback. App resumes after closing or moving to new page.
// Check token and username
updateDataFromCache(_adal.config.loginResource);
if (!_adal._renewActive && !_oauthData.isAuthenticated && _oauthData.userName) {
if (!_adal._getItem(_adal.CONSTANTS.STORAGE.FAILED_RENEW)) {
// Idtoken is expired or not present
_adal.acquireToken(_adal.config.loginResource, function (error, tokenOut) {
if (error) {
$rootScope.$broadcast('adal:loginFailure', 'auto renew failure');
} else {
if (tokenOut) {
_oauthData.isAuthenticated = true;
}
}
});
}
}
}
$timeout(function () {
updateDataFromCache(_adal.config.loginResource);
$rootScope.userInfo = _oauthData;
}, 1);
}
...
And here is a sample code which could save the tokens into cache for your reference:
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="node_modules\adal-angular\lib\adal.js"> </script>
<script src="config.js"> </script>
</head>
<body>
<div>
<button onclick="login()" >Login</button>
</div>
<script>
var authContext=new AuthenticationContext(config);
function login(){
authContext.login();
}
function init(configOptions){
if (configOptions) {
// redirect and logout_redirect are set to current location by default
var existingHash = window.location.hash;
var pathDefault = window.location.href;
if (existingHash) {
pathDefault = pathDefault.replace(existingHash, '');
}
configOptions.redirectUri = configOptions.redirectUri || pathDefault;
configOptions.postLogoutRedirectUri = configOptions.postLogoutRedirectUri || pathDefault;
// create instance with given config
} else {
throw new Error('You must set configOptions, when calling init');
}
// loginresource is used to set authenticated status
updateDataFromCache(authContext.config.loginResource);
}
var _oauthData = { isAuthenticated: false, userName: '', loginError: '', profile: '' };
var updateDataFromCache = function (resource) {
// only cache lookup here to not interrupt with events
var token = authContext.getCachedToken(resource);
_oauthData.isAuthenticated = token !== null && token.length > 0;
var user = authContext.getCachedUser() || { userName: '' };
_oauthData.userName = user.userName;
_oauthData.profile = user.profile;
_oauthData.loginError = authContext.getLoginError();
};
init(config);
function saveTokenFromHash(){
var hash = window.location.hash;
var requestInfo = authContext.getRequestInfo(hash);
if (authContext.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = authContext.getRequestInfo(hash);
authContext.saveTokenFromHash(requestInfo);
window.location.hash = '';
if (requestInfo.requestType !== authContext.REQUEST_TYPE.LOGIN) {
authContext.callback = window.parent.AuthenticationContext().callback;
}
}
}
saveTokenFromHash();
</script>
</body>
</html>