Hace poco tuve que implementar una API en PHP que pretendía integrar funcionalidades de terceros en un sitio web. Uno de los métodos que se necesitaban debía devolver la URL a la que se redirige inicialmente al usuario para que nos dé permisos de acceso a su cuenta, en principio mediante OAuth.
No hubo problema para los dos primeros casos (Gmail y Facebook), pero al empezar con Hotmail, que utiliza una librería en Javascript, era preferible abrir el popup estándar que provee Windows Live antes que mandar al usuario a una página en la que el 95% del contenido está en blanco.
Las complicaciones empezaron al intentar abrir una ventana nueva de forma “indirecta”, es decir, sin especificar el código directamente en el HTML original, sino a través de una función asignada de forma dinámica:
function wlConsent(connect_url) { window.open(connect_url, 'wl', 'width=...'); }
Dicha ventana se abría correctamente en la mayoría de los navegadores, pero algunos, los más modernos, no lo permitían debido a su bloqueador de popups.
La solución parecía sencilla, recoger el valor devuelto por el método “open” del objeto “window” y comprobar si estaba vacío:
var popup = window.open(...) if (!popup || popup.closed || typeof popup == 'undefined' || typeof popup.closed=='undefined') { // popup bloqueado }
Pero Chrome no se comporta como el resto de sus competidores. El navegador de Google requiere de una comprobación “extra” realizada desde un popup a través de la función:
function chrome_popups_permitted() { if(window.innerHeight != 0) { return true; } else return false; }
Basta pues con detectar si el navegador del usuario es Chrome y, en tal caso, abrir una ventana con el código Javascript indicado y realizar la llamada a dicha función. Si los popups están bloqueados, se redirige al usuario a la URL solicitada en vez de abrirla en una nueva ventana/pestaña.
El resultado final, encapsulado en una clase sencilla, es el siguiente:
var PopupTester = { popup : Object, url : "", name : "popup_tester", options : "width=1,height=1,left=0,top=0", open : function(url, name, options) { this.url = url if (name) this.name = name if (options) this.options = options if(this.popups_are_disabled() == true) { this.redirect_to_instruction_page(); } }, redirect_to_instruction_page : function() { document.location.href = this.url; }, popups_are_disabled : function() { this.popup = window.open("/chrome_popup_test.html", this.name, this.options); if (!this.popup || this.popup.closed || typeof this.popup == 'undefined' || typeof this.popup.closed=='undefined') { return true; } // // Chrome popup detection requires that the popup validates itself // - so we need to give // the popup time to load, then call js on the popup itself // if (navigator & (navigator.userAgent.toLowerCase()).indexOf("chrome") > -1) { var on_load_test = function(){PopupTester.test_chrome_popups();}; var timer = setTimeout(on_load_test, 200); return; } this.popup.location.href = this.url; return false; }, test_chrome_popups : function() { if (this.popup & this.popup.chrome_popups_permitted & this.popup.chrome_popups_permitted() == true) { this.popup.location.href = this.url; return true; } this.popup.close(); // // If the popup js fails - popups are blocked // this.redirect_to_instruction_page(); } };
Y un ejemplo de uso, idéntico al método original:
PopupTester.open(url, window_name, options)
Esta solución se basa en la propuesta de The Code Abode con algunas modificaciones, que permiten abrir el popup “de prueba” bajo demanda, es decir sólo en caso de necesitarlo.