postMessage() event listener not working - event-listener

I am working with a 3rd party who supplies a URL to be put into an iFrame to display some hosted video playback.
this is cross-domain
they use JWPlayer as their player of choice
I requested a way to 'know' when the video playback is complete. From reading, looks like the postMessage() callback is what many use.. and is what the 3rd vendor suggested, and mentioned they would implement.
I was given a TEST url that has this 'call back' function in it... and to see if I can could use it.
I can not seem to get any alert from the callback/listener functions?
As this is the first time I am implementing this, Im not sure if the error stems from my end or theirs?
I'm thinking it may be the path form the postMessage() function?
After firebugging the code.. I eventually fund their JS/callback set up here:
jwPI.on('complete', function(event){
playbackTime= playbackTime + (jwPI.getPosition() - positionA);
positionA=jwPI.getPosition();
parent.postMessage('EndVideo','*');
});
My side of things has the simple event listener added like so:
window.addEventListener("message", function(evt) {
//do whatever
alert("VIDEO CALLBACK FIRED");
});
My questions are:
1.) Why is this not working? a target/scope issue?
2.) Do I need to have the 3rd party vendor update the path in their postMessage() callback? where does '.parent' actually point to? (if this is an embedded iFrame?) and there are DIV's..etc..etc..etc housing the nested iFrame content?
my listener function is in the main parent file that loads this iFrame?
3.) Can I just leave it as 'as-is' and somehow change the path/target in my listener?
Solution posted:
here is a both a jQuery and JS solution
** note the jQuery approach need to use originalEvent in the scope
//jQuery approach
$(window).on("message onmessage", function(evt) {
//message
var targetData = evt.originalEvent.data;
//origin
var targetOrigin = evt.originalEvent.origin;
//check origin for security and to make Scott proud
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});
//Javascript approach
window.addEventListener("message", function(evt) {
//message
var targetData = evt.data;
//source
var targetSource = evt.source; //iframe source message stems from - doesnt work
//origin
var targetOrigin = evt.origin;
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});

(Posted an answer on behalf of the question author).
Here is a both a jQuery and JS solution. Note the jQuery approach need to use originalEvent in the scope.
//jQuery approach
$(window).on("message onmessage", function(evt) {
//message
var targetData = evt.originalEvent.data;
//origin
var targetOrigin = evt.originalEvent.origin;
//check origin for security and to make Scott proud
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});
//Javascript approach
window.addEventListener("message", function(evt) {
//message
var targetData = evt.data;
//source
var targetSource = evt.source; //iframe source message stems from - doesnt work
//origin
var targetOrigin = evt.origin;
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});

Related

Problems with WebAudio

I'm creating a research experiment that uses WebAudio API to record audio files spoken by the user.
I came up with a solution for this using recorder.js and everything was working fine... until I tried it yesterday.
I am now getting this error in Chrome:
"The AudioContext was not allowed to start. It must be resumed (or
created) after a user gesture on the page."
And it refers to this link: Web Audio API policy.
This appears to be a consequence of Chrome's new policy outlined at the link above.
So I attempted to solve the problem by using resume() like this:
var gumStream; //stream from getUserMedia()
var rec; //Recorder.js object
var input; //MediaStreamAudioSourceNode we'll be recording
// shim for AudioContext when it's not avb.
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext; //new audio context to help us record
function startUserMedia() {
var constraints = { audio: true, video:false };
audioContext.resume().then(() => { // This is the new part
console.log('context resumed successfully');
});
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js");
gumStream = stream;
input = audioContext.createMediaStreamSource(stream);
rec = new Recorder(input, {numChannels:1});
audio_recording_allowed = true;
}).catch(function(err) {
console.log("Error");
});
}
Now in the console I'm getting:
Error
context resumed successfully
And the stream is not initializing.
This happens in both Firefox and Chrome.
What do I need to do?
I just had this exact same problem! And technically, you helped me to find this answer. My error message wasn't as complete as yours for some reason and the link to those policy changes had the answer :)
Instead of resuming, it's best practise to create the audio context after the user interacted with the document (when I say best practise, if you have a look at padenot's first comment of 28 Sept 2018 on this thread, he mentions why in the first bullet point).
So instead of this:
var audioContext = new AudioContext; //new audio context to help us record
function startUserMedia() {
audioContext.resume().then(() => { // This is the new part
console.log('context resumed successfully');
});
}
Just set the audio context like this:
var audioContext;
function startUserMedia() {
if(!audioContext){
audioContext = new AudioContext;
}
}
This should work, as long as startUserMedia() is executed after some kind of user gesture.

Why explodes this window

i have a form loaded with the user data (from Json Store). When i click the first time, the form show good. But, the second time crash :
Firebug says:
Also crash, when i show other form after this form.
Any ideas ?
Edit for more info:
The code to load form is:
cargarFormularioEdicion: function(idPaciente){
var store = Ext.create('cp.store.form.Paciente',{});
store.load({params:{idUsuario: idPaciente}});
var form = Ext.create('cp.view.form.EditPaciente',{
action: 'bin/paciente/modificar.php'
});
// Ver la forma de pasar este listener al controller
form.on('afterrender',function(form,idPaciente){
form.getForm().loadRecord(store.first());
form.getForm().findField('idUsuario').setValue(idPaciente);
});
var win = Ext.create('cp.view.ui.DecoratorForm',{
aTitle: 'Editar paciente',
aForm: form
});
win.show();
}
Hypothetical solution:
Load the store with async = false.
var store = Ext.create('cp.store.form.Paciente',{});
Ext.apply(Ext.data.Connection.prototype, {
async: false
});
store.load({params:{idUsuario: idPaciente}});
Your code does not guarantee that the store is loaded at the time form.getForm().loadRecord(store.first()); is called. Since it is probably not, somewhere in the processing of loadRecord expects tries to access an undefined variable (as your error message shows) and that crashes the javascript execution. That leaves the form component with a broken internal state, and so everything is ugly from there.
You've kind of found that already by loading your store synchronously, but that's really something you should avoid. It wastes processing time by blocking a thread, and probably freeze your application for a few seconds.
The correct way of handling asynchronous events is to pass them a function that they will call back when there done. Hence the name callback function (and the joke "Hollywood principle"). This is efficient and safe.
Since the callback function can be passed in multiple ways, you need to refer to the doc. Taking your code as example, look at the doc for Ext.data.Store#load. Here's how to fix your code elegantly:
cargarFormularioEdicion: function(idPaciente){
var store = Ext.create('cp.store.form.Paciente');
store.load({
params:{idUsuario: idPaciente}
// here's how to pass a callback to Store#load
// notice you get access to some useful parameters as well!
,callback: function(records, operation, success) {
if (!success) {
// the execution will continue from here when the store is loaded
var form = Ext.create('cp.view.form.EditPaciente',{
action: 'bin/paciente/modificar.php'
});
// Ver la forma de pasar este listener al controller
form.on('afterrender',function(form,idPaciente){
form.getForm().loadRecord(store.first());
form.getForm().findField('idUsuario').setValue(idPaciente);
});
var win = Ext.create('cp.view.ui.DecoratorForm',{
aTitle: 'Editar paciente',
aForm: form
});
win.show();
} else {
// you need to handle that case
}
}
});
}
Hypothetical solution: Load the store with async = false.
var store = Ext.create('cp.store.form.Paciente',{});
Ext.apply(Ext.data.Connection.prototype, {
async: false
});
store.load({params:{idUsuario: idPaciente}});

What's the best way to cancel an upload in progress using filepicker.io?

I'm using filepicker.makeDropPane to play around with some simple uploads. One thing that I cannot find in the docs at filepicker.io's web documentation is a method for canceling an upload that is in progress.
Intuitively I feel that the onStart function should be passed an additional parameter. An object that represents the upload that has a cancel() function which when called would immediately cancel the file uploads. Something like this does not seem to be available.
we don't actually have this functionality yet, but it makes sense. I'll take a look at adding something along these lines and let you know
Here's an ugly hack I figured out to protect against unwanted returned files as well some other fancy stuff around progress and multiple files etc. You definitely want to use a static js file include so it doesn't get changed on you and break this.
Use this in your progress handler:
var event = event || window.event;
// For firefox, go and grab the XHR progress event object.
if (typeof event == 'undefined' || event == null) {
if (typeof arguments.callee.caller.arguments[0] == 'object') {
event = arguments.callee.caller.arguments[0];
}
else {
event = arguments.callee.caller.arguments.callee.caller.arguments.callee.caller.arguments[0];
}
}
var sUploadId;
if (event.type.toLowerCase() == 'progress') {
// if we haven't already tagged this XHR object (one per file), tag it now
// if we receive further progress from this file, it will already have been tagged
if (!('id' in event.currentTarget)) {
sUploadId = goUploadProgress.files.length;
event.currentTarget.id = sUploadId;
goUploadProgress.files.push({id: sUploadId, total:event.total});
}
else {
sUploadId = event.currentTarget.id;
}
// get the loaded bytes for this file
goUploadProgress.files[sUploadId].loaded = event.loaded;
}
else {
// ignore readystatechange events
return;
}
I modified this to make more sense for this context, so I may have made a logical mistake, but this is the gist. You can get the event in Firefox by looking up the call stack, and you can append an id to each file's XHR upload object, which will exist until it finishes.
I repeat, this is a hack! Don't trust that it won't break.

can't tap on item in google autocomplete list on mobile

I'm making a mobile-app using Phonegap and HTML. Now I'm using the google maps/places autocomplete feature. The problem is: if I run it in my browser on my computer everything works fine and I choose a suggestion to use out of the autocomplete list - if I deploy it on my mobile I still get suggestions but I'm not able to tap one. It seems the "suggestion-overlay" is just ignored and I can tap on the page. Is there a possibility to put focus on the list of suggestions or something that way ?
Hope someone can help me. Thanks in advance.
There is indeed a conflict with FastClick and PAC. I found that I needed to add the needsclick class to both the pac-item and all its children.
$(document).on({
'DOMNodeInserted': function() {
$('.pac-item, .pac-item span', this).addClass('needsclick');
}
}, '.pac-container');
There is currently a pull request on github, but this hasn't been merged yet.
However, you can simply use this patched version of fastclick.
The patch adds the excludeNode option which let's you exclude DOM nodes handled by fastclick via regex. This is how I used it to make google autocomplete work with fastclick:
FastClick.attach(document.body, {
excludeNode: '^pac-'
});
This reply may be too late. But might be helpful for others.
I had the same issue and after debugging for hours, I found out this issue was because of adding "FastClick" library. After removing this, it worked as usual.
So for having fastClick and google suggestions, I have added this code in geo autocomplete
jQuery.fn.addGeoComplete = function(e){
var input = this;
$(input).attr("autocomplete" , "off");
var id = input.attr("id");
$(input).on("keypress", function(e){
var input = this;
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(37.2555, -121.9245),
new google.maps.LatLng(37.2555, -121.9245));
var options = {
bounds: defaultBounds,
mapkey: "xxx"
};
//Fix for fastclick issue
var g_autocomplete = $("body > .pac-container").filter(":visible");
g_autocomplete.bind('DOMNodeInserted DOMNodeRemoved', function(event) {
$(".pac-item", this).addClass("needsclick");
});
//End of fix
autocomplete = new google.maps.places.Autocomplete(document.getElementById(id), options);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
//Handle place selection
});
});
}
if you are using Framework 7, it has a custom implementation of FastClicks. Instead of the needsclick class, F7 has no-fastclick. The function below is how it is implemented in F7:
function targetNeedsFastClick(el) {
var $el = $(el);
if (el.nodeName.toLowerCase() === 'input' && el.type === 'file') return false;
if ($el.hasClass('no-fastclick') || $el.parents('.no-fastclick').length > 0) return false;
return true;
}
So as suggested in other comments, you will only have to add the .no-fastclick class to .pac-item and in all its children
I was having the same problem,
I realized what the problem was that probably the focusout event of pac-container happens before the tap event of the pac-item (only in phonegap built-in browser).
The only way I could solve this, is to add padding-bottom to the input when it is focused and change the top attribute of the pac-container, so that the pac-container resides within the borders of the input.
Therefore when user clicks on item in list the focusout event is not fired.
It's dirty, but it works
worked perfectly for me :
$(document).on({
'DOMNodeInserted': function() {
$('.pac-item, .pac-item span', this).addClass('needsclick');
}
}, '.pac-container');
Configuration: Cordova / iOS iphone 5

JavaScript: Event listener on iFrame resize / height attribute change (FB comment box)

I have the following problem: I need to add an event listener for an iframe (a Facebook comment box) when it changes its height.
I can not access or change the contentWindow because it is cross domain. But there must be a callback function or something that changes the height attribute.
Is there a way to add an event listener on attribute changes or something? I alreade tried onResize and onChange.
I'm getting crazy with that... Anyone has an idea?
Thank you so much!!
Short answer: No.
However, you can use "postMessage" and "receiveMessage" to send from one iframe to another cross domain. (Of course, only if you have access to the iframed content - I suspect not as it's on facebook.)
In any case... for future help....
(on the iframed page)
var ii = {}
ii.window_height = 800;
var sendHeight = function () {
var window_height = $('body').outerHeight(true);
if (window_height != ii.window_height) {
ii.window_height = window_height;
window.parent.postMessage(ii.window_height, "http://containerDomain.com");
}
}
setInterval(sendHeight, 2000);
(on the container page)
function receiveMessage(evt) {
if (evt.origin === 'https://iframedDomain.com')
{
var iframe_content_height = evt.data;
$('#iframe_form').animate({height: iframe_content_height });
}
}
if ($.browser.msie) {
window.attachEvent('onmessage', receiveMessage);
} else {
window.addEventListener('message', receiveMessage, false);
}
Remember to change the domains in each script.
Note: that's using jQuery - It works, but I'm sure someone can write that better then me? Also not too proud of the interval checking the height... might update if i can.