I am building an add-on as a project for Firefox using JPM and was wondering how I could enable indefinite permissions for the user?
e.g.
I have the below code which prompts the user for permission on every website:
function HTT(theBody,theIcon,theTitle) {
var options = {
body: theBody,
icon: theIcon
}
if (!("Notification" in window)) {
alert("This browser does not support HTT notifications");
}
else if (Notification.permission === "granted") {
var n = new Notification(theTitle,options);
Date.now() + 30000;
//setTimeout(n.close.bind(n), 4000);
}
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
if (permission === "granted") {
var n = new Notification(theTitle,options);
Date.now() + 30000;
//setTimeout(n.close.bind(n), 4000);
}
});
}
}
What I would like to do is ask for permission once ONLY. How could I achieve this as I have tried a number of different options?
FYI - The purpose of the add-on is the display a notification, but this is the permission side of the code.
Thanks
Related
I'm trying to create a website that can connect to metamask.
It should automatically open the metamask extension to allow the user to confirm the connection upon entering the site, however it just says "attempting to connect" and metamask never opens.
Here is my connectWallet code - there is also a button that runs connectWallet onclick
<script>
function connectWallet() {
// window.addEventListener('load', function () {
document.getElementById("demo").innerHTML = "ATTEMPTING TO CONNECT!";
if (window.ethereum) {
window.web3 = new Web3(ethereum);
ethereum.enable()
.then(() => {
console.log("Ethereum enabled");
web3.eth.getAccounts(function (err, acc) {
if (err != null) {
self.setStatus("There was an error fetching your accounts");
return;
}
if (acc.length > 0) {
console.log(acc);
walletID = acc[0]
document.getElementById("demo").innerHTML = "CONNECTED!";
getNFTsOfUser();
return;
}
});
})
.catch(() => {
console.warn('User didn\'t allow access to accounts.');
document.getElementById("demo").innerHTML = "CONNECTION REJECTED!";
waitLogin();
});
} else {
console.log("Non-Ethereum browser detected. You should consider installing MetaMask.");
document.getElementById("demo").innerHTML = "METAMASK NOT FOUND! PLEASE INSTALL OR USE A DAPP!";
}
// })
}
I ran the sample custom receiver. I am running it using ngrok but i see in the console that its not connecting to web socket. Any help is appreciated: Here is my receiver code and screenshot
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();
// Customize the license url for playback
playbackConfig.licenseUrl = 'https://wv-keyos.licensekeyserver.com/';
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
requestInfo.headers = {
'customdata': '<custom data>'
};
};
// Update playback config licenseUrl according to provided value in load request.
context.getPlayerManager().setMediaPlaybackInfoHandler((loadRequest, playbackConfig) => {
if (loadRequest.media.customData && loadRequest.media.customData.licenseUrl) {
playbackConfig.licenseUrl = loadRequest.media.customData.licenseUrl;
}
return playbackConfig;
});
function makeRequest (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(JSON.parse(xhr.response));
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
castDebugLogger.info('MyAPP.LOG', 'Intercepting LOAD request');
if (request.media && request.media.entity) {
request.media.contentId = request.media.entity;
}
return new Promise((resolve, reject) => {
if(request.media.contentType == 'video/mp4') {
return resolve(request);
}
// Fetch content repository by requested contentId
makeRequest('GET', 'https://tse-summit.firebaseio.com/content.json?orderBy=%22$key%22&equalTo=%22'+ request.media.contentId + '%22')
.then(function (data) {
var item = data[request.media.contentId];
if(!item) {
// Content could not be found in repository
castDebugLogger.error('MyAPP.LOG', 'Content not found');
reject();
} else {
// Adjusting request to make requested content playable
request.media.contentId = item.stream.hls;
request.media.contentType = 'application/x-mpegurl';
castDebugLogger.warn('MyAPP.LOG', 'Playable URL:', request.media.contentId);
// Add metadata
var metadata = new cast.framework.messages.MovieMediaMetadata();
metadata.metadataType = cast.framework.messages.MetadataType.MOVIE;
metadata.title = item.title;
metadata.subtitle = item.author;
request.media.metadata = metadata;
resolve(request);
}
});
});
});
/** Debug Logger **/
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
// Enable debug logger and show a warning on receiver
// NOTE: make sure it is disabled on production
castDebugLogger.setEnabled(true);
playerManager.addEventListener(
cast.framework.events.category.CORE,
event => {
castDebugLogger.info('ANALYTICS', 'CORE EVENT:', event);
});
// Set verbosity level for custom tags
castDebugLogger.loggerLevelByTags = {
'MyAPP.LOG': cast.framework.LoggerLevel.WARNING,
'ANALYTICS': cast.framework.LoggerLevel.INFO,
};
/** Optimizing for smart displays **/
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);
const touchControls = cast.framework.ui.Controls.getInstance();
let browseItems = getBrwoseItems();
function getBrwoseItems() {
let data = '"video": { \
"author": "The Blender Project", \
"description": "Grumpy Bunny is grumpy", \
"poster": "https://storage.googleapis.com/tse-summit.appspot.com/bbb/poster.png", \
"prog": "https://storage.googleapis.com/tse-summit.appspot.com/bbb/bbb-prog.mp4", \
"stream": { \
"dash": "https://d8dbsji255dut.cloudfront.net/drm-test/4K-Gaming-Sample.mpd", \
"hls": "https://d8dbsji255dut.cloudfront.net/drm-test/4K-Gaming-Sample.m3u8" \
}, \
"title": "Big Buck Bunny" \
}';
let browseItems = [];
for (let key in data) {
let item = new cast.framework.ui.BrowseItem();
item.entity = key;
item.title = data[key].title;
item.subtitle = data[key].description;
item.image = new cast.framework.messages.Image(data[key].poster);
item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
browseItems.push(item);
}
return browseItems;
}
let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio =
cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;
playerDataBinder.addEventListener(
cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
(e) => {
if (!e.value) return;
// Clear default buttons and re-assign
touchControls.clearDefaultSlotAssignments();
touchControls.assignButton(
cast.framework.ui.ControlsSlot.SLOT_1,
cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
);
// Media browse
touchControls.setBrowseContent(browseContent);
});
// context.start({ touchScreenOptimizedApp: true });
context.start({playbackConfig: playbackConfig});
I have already registered for Google Cast SDK Developer Console and created a unpublished app and added my chrome cast device as well.
You are seeing these errors because you are loading the receiver through a web browser directly. The usual flow of a cast session is that a "sender" (web browser, iOS device, android device) will initiate a cast session with the hosted receiver, and thereby beginning the socket connection. Right now, you only have a receiver loading, nothing has initiated the session.
One way to test this is to plug in a Chromecast or cast enabled device (most Android TV's have Chromecast built-in too!), and use a valid sender to connect to your receiver.
Google have built an awesome tool to help with Chromecast development, it's a shame it's not publicised more. You can find it here: https://casttool-1287.appspot.com/cactesttool/index.html
If you're wanting to really nail down your Chromecast development skills, I personally recommend that you checkout:
Google Codelabs for Cast, these have some really helpful walkthroughs. https://codelabs.developers.google.com/?cat=Cast
The Google Cast github repo, has some great examples. https://github.com/googlecast
Note: This is by no means a detailed explanation of how cast sessions are actually initiated, some very smart people have done some digging into how Chromecast sessions work, and if you're interested checkout Romain Picard's writeup at https://blog.oakbits.com/google-cast-protocol-overview.html
We have single sign on enabled for our MS Dynamics 365 CRM instance to make a calls to an API hosted in Azure. On launch of CRM we have the following JavaScript that executes. This works most of the time, but on occasion we get "Invalid argument" popup. I am relatively new to using Adal.js and have no idea what is causing this. Any trouble shooting tips appreciated. Thanks in advance.
config = {
ApiUrl: configData["ApiUrl"],
SubscriptionKey: configData["SubscriptionKey"],
trace: configData["trace"],
AcceptHeader: configData["AcceptHeader"],
ContentTypeHeader: configData["ContentTypeHeader"],
tenant: configData["tenant"],
clientId: configData["clientId"],
tokenStoreUrl: configData["tokenStoreUrl"],
cacheLocation: configData["cacheLocation"],
GraphApi: configData["GraphApi"]
};
// Check For & Handle Redirect From AAD After Login
authContext = new window.AuthenticationContext(config);
var isCallback = authContext.isCallback(window.location.hash);
if (isCallback) {
authContext.handleWindowCallback();
}
var loginError = authContext.getLoginError();
if (loginError) {
console.log('ERROR:\n\n' + loginError);
}
authContext.popUp = true;
if (isCallback && !loginError) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
var user = authContext.getCachedUser();
if (!user) {
authContext.clearCache();
sessionStorage["adal.login.request"] = "";
authContext.login();
}
window.parent.authContext = authContext;
It has been a while since I last looked at this, however I managed to get it resolved at the time. I implemented a locking mechanism, to ensure the login completes before trying to obtain a token.
Here is the updated code:
config = {
ApiUrl: configData["ApiUrl"],
SubscriptionKey: configData["SubscriptionKey"],
trace: configData["trace"],
AcceptHeader: configData["AcceptHeader"],
ContentTypeHeader: configData["ContentTypeHeader"],
tenant: configData["tenant"],
clientId: configData["clientId"],
tokenStoreUrl: configData["tokenStoreUrl"],
cacheLocation: configData["cacheLocation"],
GraphApi: configData["GraphApi"],
loadFrameTimeout: 10000
};
// Check For & Handle Redirect From AAD After Login
authContext = new window.AuthenticationContext(config);
var isCallback = authContext.isCallback(window.location.hash);
if (isCallback) {
authContext.handleWindowCallback();
}
var loginError = authContext.getLoginError();
if (loginError) {
// TODO: Handle errors signing in and getting tokens
console.log('ERROR:\n\n' + loginError);
}
authContext.popUp = true;
if (isCallback && !loginError) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
var user = authContext.getCachedUser();
if (!user) {
authContext.clearCache();
sessionStorage["adal.login.request"] = "";
authContext.callback = function (error, token, msg) {
// remove lock
window.top.loginLock = null;
if (!!token) {
getGraphApiTokenAndUpdateUser(authContext);
}
else {
console.log('ERROR:\n\n' + error);
}
};
if (typeof (window.top.loginLock) == "undefined" || window.top.loginLock == null) {
// Create lock
window.top.loginLock = true;
authContext.login();
}
}
window.parent.authContext = authContext;
I have some circular dependencies in my Titanium application like so:
index.js
var Auth = require('Auth')
Auth.js
var PopUp = require('PopUp');
function isLoggedIn() {
// some logic e.g. return userName !== null
};
function authorise() {
if (isLoggedIn()) {
return true;
} else {
return PopUp.authorise();
}
}
PopUp
var Auth = require("Auth");
function authorise() {
// some code asking user to login
}
function showSecurePopUp() {
if (Auth.isLoggedIn()) {
// show secure pop up
}
}
As you can see we have a circular dependency. Auth needs PopUp and PopUp needs Auth.
This creates a circular dependency and thus the following error message:
[ERROR] [iphone, 10.3.3, 192.168.0.64]
Type: RangeError
Message: Maximum call stack size exceeded.
File: /iphone/Auth.js.js
Line: 24
How can I solve the issue of circular dependencies in a Titanium Alloy app?
I think this could be the way, you do the following changes in you project and this should solve the problem.
Alloy.js
var Auth = require("Auth");
var PopUp = require('PopUp');
Index.js
Auth.authorise();
Auth.js
var isLoggedIn = function() {
// some logic e.g. return userName !== null
Ti.API.info('isLoggedIn');
return false;
};
exports.authorise = function() {
if (isLoggedIn()) {
Ti.API.info('authorize isloggedIn');
return true;
} else {
Ti.API.info('authorize not logged In');
return PopUp.authorise();
}
};
exports.isLoggedIn = isLoggedIn;
PopUp.js
exports.authorise =function () {
// some code asking user to login
Ti.API.info('authorize funcition popup ' + Auth.isLoggedIn());
};
function showSecurePopUp() {
if (Auth.isLoggedIn()) {
// show secure pop up
Ti.API.info('isLoggedIn show secure popup');
}
}
Let me know if this works fine and if this is what you wanted. Also if you have some other approach that solves the problem, then let me know that also.
Good Luck & Cheers
Ashish Sebastian
I am using jQuery idle timeout plugin by Eric Hynds
My question is simple but I know there won't be any simple answer to this.
The plugin works great when the website is opened in only one tab. What I want to do is when user opens the website in any number of tabs it should there should only one background timer but the info message should be shown on all tabs.
Consider for example user opens a website in 3 different tabs but actively uses only one tab(obviously) so currently the plugin senses that user is inactive on that tab for specified time and logs him out which is not correct as user still actively using other tab.
I know I have to put some hacks somewhere but really dont understand where and how. If anyone had already done this it would really help me alot. Also any suggestions are most welcome. Pls help guys.
I had the same issue! An idle timer can communicate (stay in sync) across multiple windows & tabs using localStorage variables. Most modern browsers support this feature. On github, marcuswestin/store.js provides good functionality with 'fallback' behavior for older browsers.
Here is the 'testing' code for an idleTimer plugin which provides synchronized windows/tabs (must all be within the same domain). It sets 2 localStorage variables to track the state of the user's session.
You can see a demo of this code here. Open multiple windows/tabs and observe.
http://jillelaine.github.io/jquery-idleTimeout/
/**
* This work is licensed under the MIT License
*
* Configurable idle (no activity) timer and logout redirect for jQuery.
* Works across multiple windows, tabs and iframes from the same domain.
*
* Dependencies: JQuery v1.7+, JQuery UI, store.js from https://github.com/marcuswestin/store.js - v1.3.4+
*
* Commented and console logged for debugging with Firefox & Firebug or similar
* version 1.0.6
**/
/*global jQuery: false, document: false, store: false, clearInterval: false, setInterval: false, setTimeout: false, window: false, alert: false, console: false*/
/*jslint indent: 2, sloppy: true, plusplus: true*/
(function ($) {
$.fn.idleTimeout = function (options) {
console.log('start');
//##############################
//## Configuration Variables
//##############################
var defaults = {
idleTimeLimit: 30000, // 30 seconds for testing. 'No activity' time limit in milliseconds. 1200000 = 20 Minutes
dialogDisplayLimit: 20000, // 20 seconds for testing. Time to display the warning dialog before redirect (and optional callback) in milliseconds. 180000 = 3 Minutes
redirectUrl: '/logout', // redirect to this url on timeout logout. Set to "redirectUrl: false" to disable redirect
// optional custom callback to perform before redirect
customCallback: false, // set to false for no customCallback
// customCallback: function () { // define optional custom js function
// perform custom action before logout
// },
// configure which activity events to detect
// http://www.quirksmode.org/dom/events/
// https://developer.mozilla.org/en-US/docs/Web/Reference/Events
activityEvents: 'click keypress scroll wheel mousewheel', // separate each event with a space
//dialog box configuration
dialogTitle: 'Session Expiration Warning',
dialogText: 'Because you have been inactive, your session is about to expire.',
// server-side session keep-alive timer
sessionKeepAliveTimer: 600000 // Ping the server at this interval in milliseconds. 600000 = 10 Minutes
// sessionKeepAliveTimer: false // Set to false to disable pings
},
//##############################
//## Private Variables
//##############################
opts = $.extend(defaults, options),
checkHeartbeat = 2000, // frequency to check for timeouts - 2000 = 2 seconds
origTitle = document.title, // save original browser title
sessionKeepAliveUrl = window.location.href, // set URL to ping to user's current window
keepSessionAlive, activityDetector,
idleTimer, remainingTimer, checkIdleTimeout, idleTimerLastActivity, startIdleTimer, stopIdleTimer,
openWarningDialog, dialogTimer, checkDialogTimeout, startDialogTimer, stopDialogTimer, isDialogOpen, destroyWarningDialog,
countdownDisplay, logoutUser,
checkForIframes, includeIframes, attachEventIframe; // iframe functionality
//##############################
//## Private Functions
//##############################
keepSessionAlive = function () {
if (opts.sessionKeepAliveTimer) {
var keepSession = function () {
if (idleTimerLastActivity === store.get('idleTimerLastActivity')) {
console.log('keep session alive function');
$.get(sessionKeepAliveUrl);
}
};
setInterval(keepSession, opts.sessionKeepAliveTimer);
}
};
activityDetector = function () {
$('body').on(opts.activityEvents, function () {
if (isDialogOpen() !== true) {
console.log('activity detected');
startIdleTimer();
} else {
console.log('dialog open. activity ignored');
}
});
};
checkIdleTimeout = function () {
var timeNow = $.now(), timeIdleTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit);
if (timeNow > timeIdleTimeout) {
console.log('timeNow: ' + timeNow + ' > idle ' + timeIdleTimeout);
if (isDialogOpen() !== true) {
console.log('dialog is not open & will be opened');
openWarningDialog();
startDialogTimer();
}
} else if (store.get('idleTimerLoggedOut') === true) { //a 'manual' user logout?
logoutUser();
} else {
console.log('idle not yet timed out');
if (isDialogOpen() === true) {
console.log('dialog is open & will be closed');
destroyWarningDialog();
stopDialogTimer();
}
}
};
startIdleTimer = function () {
stopIdleTimer();
idleTimerLastActivity = $.now();
store.set('idleTimerLastActivity', idleTimerLastActivity);
console.log('start idle timer: ' + idleTimerLastActivity);
idleTimer = setInterval(checkIdleTimeout, checkHeartbeat);
};
stopIdleTimer = function () {
clearInterval(idleTimer);
};
openWarningDialog = function () {
var dialogContent = "<div id='idletimer_warning_dialog'><p>" + opts.dialogText + "</p><p style='display:inline'>Time remaining: <div style='display:inline' id='countdownDisplay'></div></p></div>";
$(dialogContent).dialog({
buttons: {
"Stay Logged In": function () {
console.log('Stay Logged In button clicked');
destroyWarningDialog();
stopDialogTimer();
startIdleTimer();
},
"Log Out Now": function () {
console.log('Log Out Now button clicked');
logoutUser();
}
},
closeOnEscape: false,
modal: true,
title: opts.dialogTitle
});
// hide the dialog's upper right corner "x" close button
$('.ui-dialog-titlebar-close').css('display', 'none');
// start the countdown display
countdownDisplay();
// change title bar to warning message
document.title = opts.dialogTitle;
};
checkDialogTimeout = function () {
var timeNow = $.now(), timeDialogTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit + opts.dialogDisplayLimit);
if ((timeNow > timeDialogTimeout) || (store.get('idleTimerLoggedOut') === true)) {
console.log('timeNow: ' + timeNow + ' > dialog' + timeDialogTimeout);
logoutUser();
} else {
console.log('dialog not yet timed out');
}
};
startDialogTimer = function () {
dialogTimer = setInterval(checkDialogTimeout, checkHeartbeat);
};
stopDialogTimer = function () {
clearInterval(dialogTimer);
clearInterval(remainingTimer);
};
isDialogOpen = function () {
var dialogOpen = $("#idletimer_warning_dialog").is(":visible");
if (dialogOpen === true) {
return true;
}
return false;
};
destroyWarningDialog = function () {
console.log('dialog destroyed');
$(".ui-dialog-content").dialog('destroy').remove();
document.title = origTitle;
};
// display remaining time on warning dialog
countdownDisplay = function () {
var dialogDisplaySeconds = opts.dialogDisplayLimit / 1000, mins, secs;
remainingTimer = setInterval(function () {
mins = Math.floor(dialogDisplaySeconds / 60); // minutes
if (mins < 10) { mins = '0' + mins; }
secs = dialogDisplaySeconds - (mins * 60); // seconds
if (secs < 10) { secs = '0' + secs; }
$('#countdownDisplay').html(mins + ':' + secs);
dialogDisplaySeconds -= 1;
}, 1000);
};
logoutUser = function () {
console.log('logout function');
store.set('idleTimerLoggedOut', true);
if (opts.customCallback) {
console.log('logout function custom callback');
opts.customCallback();
}
if (opts.redirectUrl) {
console.log('logout function redirect to URL');
window.location.href = opts.redirectUrl;
}
};
// document must be in readyState 'complete' before looking for iframes
checkForIframes = function () {
var docReadyCheck, isDocReady;
docReadyCheck = function () {
if (document.readyState === "complete") {
console.log('check for iframes, now that the document is complete');
clearInterval(isDocReady);
includeIframes();
}
};
isDocReady = setInterval(docReadyCheck, 1000);
};
// look for iframes
includeIframes = function () {
console.log('include iframes start');
var foundIframes = document.getElementsByTagName('iframe'), index, iframeItem;
if (foundIframes.length > 0) { //at least one iframe found
console.log('iframes found: ' + foundIframes.length);
// attach events to each iframe found
for (index = 0; index < foundIframes.length; index++) {
iframeItem = foundIframes.item(index);
if (iframeItem.attachEvent) { // IE < 11. Returns a boolean true/false
console.log('attach event to iframe. Browser IE < 11');
iframeItem.attachEvent('onload', attachEventIframe(index));
} else { // IE >= 11 and FF, etc.
console.log('attach event to iframe. Browser NOT IE < 11');
iframeItem.addEventListener('load', attachEventIframe(index), false);
}
} // end for loop
} // end if any iframes
};
// attach events to each iframe
attachEventIframe = function (index) {
var iframe = $('iframe:eq(' + index + ')').contents().find('html');
iframe.on(opts.activityEvents, function (event) {
console.log('bubbling iframe activity event to body of page');
$('body').trigger(event);
});
};
//###############################
// Build & Return the instance of the item as a plugin
// This is your construct.
//###############################
return this.each(function () {
if (store.enabled) {
idleTimerLastActivity = $.now();
store.set('idleTimerLastActivity', idleTimerLastActivity);
store.set('idleTimerLoggedOut', false);
} else {
alert('Please disable "Private Mode", or upgrade to a modern browser. Or perhaps a dependent file missing. Please see: https://github.com/marcuswestin/store.js');
}
activityDetector();
keepSessionAlive();
startIdleTimer();
checkForIframes();
});
};
}(jQuery));