How to add custom event to Javascript "SDK" - javascript

I'm getting more into JS development on my own personal time and for this created a sort of SDK to log into openID connect implementation ( very basic, only implicit flow and code flow). This post is about the client side JS for the implicit flow.
I have successfully created a small SDK that allows me to launch the login request and monitor the pop up for the finished log in (or window close). In the end I now want to add a custom event so that other code can subscribe and be notified when this happens. I have been unable to get this to work. So my question is, how can I do it given the code posted, and of course if you have any suggestions on bettering my code, I'll gladly accept them.
Here is the code:
var mysystemID = {
authorizationURL:"MYRUL_TO_SERVER/oauth/v2/authorize",
client_id:"MYCLIENTID",
redirect_uri:window.location.protocol+"//"+window.location.host+"/callback",
response_type:"id_token token",
windowref : undefined,
intervalID : null,
access_token:"",
id_token:"null",
parser:document.createElement('a'),
checkLogin : function(){
if (this.windowref==undefined){
return false;
}
if (this.windowref.closed){
console.log("window closed")
clearInterval(this.intervalID)
this.intervalID=null
}
try{
this.parser.href = this.windowref.location.href;
if((this.parser.protocol + "//" +this.parser.host + this.parser.pathname) == this.redirect_uri)
{
console.log("login detected")
clearInterval(this.intervalID)
this.intervalID=null
this.windowref.close()
var obj = {}
var str = this.parser.hash.substring(1)
$.each(str.split("&"),function(index,value){
var pieces = value.split("=")
obj[pieces[0]] = pieces[1]
})
console.log(obj)
this.access_token = obj.token
this.id_token = JSON.parse(atob(obj.id_token.split(".")[1]))
// var event = new CustomEvent("my_login", {"detail":{"access_token":this.id_token}});
// this.dispatchEvent(event);
console.log(this.id_token)
}
}catch(e){
//do nothing
}
}
}
mysystemID.login_process = function(){
var scope = $('#scope').val() ||"openid"
var uri = this.authorizationURL+"?response_type="+encodeURIComponent(this.response_type)
+"&client_id=" + this.client_id +"&redirect_uri="+encodeURIComponent(this.redirect_uri)
+"&scope="+encodeURIComponent(scope)+"&nonce="+Math.floor( Math.random()*99999);
console.log(uri)
this.windowref = window.open(uri,
"login with mysystem
ID","width=400, height=600")
if (this.windowref!=null){
this.intervalID = setInterval(this.checkLogin.bind(mysystemID),500)
}
}.bind(mysystemID)
mysystemID.logout = function(){
this.access_token = ""
this.id_token = ""
}.bind(mysystemID)
mysystemID.isLoggedIn = function(){
return this.access_token!=""
}.bind(mysystemID)
A few notes, I rely on jQuery for some things,
and I instantiate it like so:
$(document).ready(function(){
$('a#login_push').on("click",mysystemID.login_process)
});
Within the code you can see my attempt at the event:
// var event = new CustomEvent("my_login", {"detail":{"access_token":this.id_token}});
// this.dispatchEvent(event);
the listener had been chained to the
$(document).ready(function(){
$('a#login_push').on("click",mysystemID.login_process).on("my_login",function(e,data){console.log('logged in fired')});
});

Related

Does BroadcastChannel work on the same page?

My web page has
var bc = new BroadcastChannel('Consumer');
bc.onmessage = function(event) {
alert("a");
}
bc.postMessage("hello");
It broadcasts a message, and the page is also required to receive the same message.
However it doesn't work. Did I miss anything?
You can create two instances of BroadcastChannel on your page. One can act as a broadcaster for messages, the other one for receiving messages.
var broadcaster = new BroadcastChannel('Consumer');
var messageReceiver= new BroadcastChannel('Consumer');
messageReceiver.onmessage = function(event) {
alert(event.data);
}
broadcaster.postMessage("hello");
See this in action: https://jsfiddle.net/h56d3y27/
Or wrapped in a reusable class:
(note: class is not supported by all browsers. See : https://caniuse.com/#search=class for browser compatibility)
class AllInclusiveBroadcaster {
constructor(listener, channelName) {
if (!channelName) channelName = "channel";
this.broadcaster = new BroadcastChannel(channelName);
this.messageReceiver = new BroadcastChannel(channelName);
this.messageReceiver.onmessage = (event) => {
listener(event.data);
}
}
postmessage(data) {
this.broadcaster.postMessage(data);
}
}
var broadcaster = new AllInclusiveBroadcaster((data) => alert(data));
broadcaster.postmessage("Hello BroadcastChannel");
See this also in action a JSFiddle
You could dispatch an event (call it what you like) to, say, document, with the same data ... then have a single handler that listens for BroadcastChannel messages and to the event name you created above
in the following, the code creates and listens for fakeBroadcastMessage
created a function to send both the bc message and the "local" message
var bc = new BroadcastChannel('Consumer');
function handleBroadcastMessage(event) {
// do things here
}
bc.addEventHandler('message', handleBroadcastMessage);
document.addEventListener('fakeBroadcastMessage', handleBroadcastMessage);
function sendMessage(data) {
bc.postMessage(data);
var ev = new Event('fakeBroadcastMessage');
ev.data = data;
document.dispatchEvent(ev);
}
sendMessage('hello');

Part of the javascript is not running

The JS below runs accordingly, but it never hits the last function (showAllTabIdRedirect). Any idea why? Is it my syntax? I am trying to run the first function that grabs the primary tab id and then use that to pass along some other functions. In the end, I would redirect the user as well as refresh a specific tab.
<script>
function refreshDetailsTab() {
sforce.console.getEnclosingPrimaryTabId(focusDetailSubtabRedirect);
var formsId;
var currentUrl = window.location.href;
if (currentUrl) {
formsId = currentUrl.split('?formId=')[1];
} else {
return;
}
window.location = '/' + formsId;
debugger;
};
sforce.console.getEnclosingPrimaryTabId(focusDetailSubtabRedirect);
var focusDetailSubtabRedirect = function showTabIdRedirect(result) {
// window.onload = function showTabIdV1(result) {
//alert('2222');
var primaryTabID = result.id;
sforce.console.getSubtabIds(primaryTabID , showAllTabIdRedirect);
debugger;
}
var showAllTabIdRedirect = function showAllTabIdRedirect(result2) {
// alert('33333');
var firstSubTab = result2.ids[0];
sforce.console.refreshSubtabById(firstSubTab, false);
debugger;
//alert('Subtab IDs=====: ' + result.ids[0]);
};
window.onload = refreshDetailsTab;
</script>
You can check for successful completion of those methods. It's possible 'getSubtabIds' was at halt for some reason and that would cause the failure of calling the callback function 'showAllTabIdRedirect '.
See the documentation here for getSubtabIds
I think it has something to do with the window.location triggering first. It redirects the user before the other JS can load.

"Unable to load http://url status:0" error in onbeforeunload-method

may someone of you can help me to find this problem?
I've got an xpage with client-side js-code included which should be executed when you decide to leave the page. In the client-side js you refer to a button and click it automatically. This button got some server-side js code included and change the flag from a document from ("opened by ..." to "").
The thing is that somehow the client-side js did not work in all different browsers except the current IE (10.0.5) and throws the error:
unable to load http://urlofthedocument/... status:0
The funny thing about this is, when I insert an alert()-method right after the click()-method everything works fine in every browser. But as I don't want to include this alert statement I figure out there must be something different to avoid this. (A short pause instead of the alert-method also did not work.)
My CS JS-Code:
window.onbeforeunload = WarnEditMode;
function WarnEditMode(){
if(needUnloadConfirm == true){
var el = window.document.getElementById("#{id:Hidden4SSJS}");
el.click();
//document.open();
//document.write(el);
//document.close();
//alert("You're about to leave the page");
//pause(5000);
}
}
function pause(millis){
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < millis)
}
This refers to to button, which executes following SS JS code, after it is clicked:
try{
print("Hidden4SSJS-Button-Test # Person");
var db:NotesDatabase = database;
var agt:NotesAgent;
var doc:NotesDocument = XPPersonDoc.getDocument()
agt = db.getAgent("(XPUnlockDocument)");
agt.run(doc.getNoteID());
}catch(e){
_dump(e);
}
May you guys can help me with this?
I would do this using the XSP object with a hidden computed field (and not your special button)...
Something like this:
function WarnEditMode(){
if(needUnloadConfirm == true){
XSP.partialRefreshGet("#{id:unlockDocCF1}", {
params: {
'$$xspsubmitvalue': 'needToUnlock'
},
onComplete: function () {
alert('You are about to leave this page and the document has been unlocked.');
},
onError : function (e) {
alert('You are about to leave this page and the document has NOT been unlocked.\n' + e);
}
);
}
pause(5000);
}
Then the computed field's javascript would be something like this:
try{
var sval = #Explode(context.getSubmittedValue(), ',');
if (sval == null) return result + " no action.";
if (!"needToUnlock".equals(sval[0])) return result + " no action.";
print("Hidden4SSJS-Button-Test # Person");
var db:NotesDatabase = database;
var agt:NotesAgent;
var doc:NotesDocument = XPPersonDoc.getDocument()
agt = db.getAgent("(XPUnlockDocument)");
agt.run(doc.getNoteID());
return 'document unlocked.';
}catch(e){
_dump(e);
}

Javascript, window.addEventListener('blur') won't work

I would like to set a timeout javascript.
If the browser stays on browser, it redirect to fallbackLink.
If the browser loses the focus, it expire the timeout and doesn't redirect to fallbackLink
here is my script.
<script>
(function(){
var fallbackLink = '<?=$info['failed_url']?>'+window.location.search+window.location.hash;
var isiOS = navigator.userAgent.match('iPad') || navigator.userAgent.match('iPhone'),
isAndroid = navigator.userAgent.match('Android');
if (isiOS) {
document.getElementById('loader').src = '<?=$info['scheme']?>://'+window.location.search+window.location.hash;
}
var countdown = window.setTimeout(function (){
window.location.replace(fallbackLink);
}, 1000);
window.addEventListener("blur", function(){
window.clearTimeout(countdown)
}, false);
})();
</script>
Somehow it does redirect even though the browser is out of focus.
I need to stop to redirect when the screen doesn't focus on web browser.
Thank you.
Apparently the only way to handle blur event on window objects is using window.onblur.
You can try to keep old handlers doing this:
var concatFunctions = function(fn1, fn2){
return function(){
var args = Array.prototype.slice.call(arguments, 0);
try{
fn1.apply(this, args);
} catch(err) { console.error(err); }
fn2.apply(this, args);
};
};
window.onblur = concatFunction(window.onblur, function(){
// do your stuff
});

Add-on Builder: Multiple Workers Using port?

Referring to this question: Add-on Builder: ContentScript and back to Addon code?
Here is my addon code:
var widget = widgets.Widget({
id: "addon",
contentURL: data.url("icon.png"),
onClick: function() {
var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({contentScriptFile: [data.url("jquery.js"), data.url("myScript.js")]});
workers.push(worker);
}
}
});
And here is myScript.js:
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
self.port.emit('got-url', {url: url});
}
Now that I have multiple workers where do I put
worker.port.on('got-url', function(data) {
worker.tab.url = data.url;
});
Since in the other question I only had one worker but now I have an array of workers.
The code would be:
// main.js:
var data = require("self").data;
var windows = require("windows").browserWindows;
var widget = require("widget").Widget({
id: "addon",
label: "Some label",
contentURL: data.url("favicon.png"),
onClick: function() {
//var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({
contentScriptFile: [data.url("jquery.js"),
data.url("inject.js")]
});
worker.port.on('got-url', function(data) {
console.log(data.url);
// worker.tab.url = data.url;
});
worker.port.emit('init', true);
console.log("got here");
//workers.push(worker);
}
}
});
// inject.js
$(function() {
self.port.on('init', function() {
console.log('in init');
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
console.log('injected!');
self.port.emit('got-url', {url: url});
}
});
});
Edit: sorry, should have actually run the code, we had a timing issue there where the content script was injected before the worker listener was set up, so the listener was not yet created when the 'got-url' event was emitted. I work around this by deferring any action in the content script until the 'init' event is emitted into the content script.
Here's a working example on builder:
https://builder.addons.mozilla.org/addon/1045470/latest/
The remaining issue with this example is that there is no way to tell if a tab has been injected by our add-on, so we will 'leak' or use more memory every time the widget is clicked. A better approach might be to inject the content script using a page-mod when it is loaded, and only emit the 'init' event in the widget's onclick handler.

Categories

Resources