geoportal-access-lib
Version:
French Geoportal resources access library
248 lines (220 loc) • 10.1 kB
JavaScript
/**
* JSONP : Implémentation du protocole JSONP de la plateforme Géoportail
*
* @module JSONP
* @private
* @alias Gp.Protocols.JSONP
*/
import Logger from "../Utils/LoggerByDefault";
// cf. https://github.com/sobstel/jsonp.js
var JSONP = {
/**
* Construction d'un identifiant statique basé sur le timestamp,
* et qui s'incremente de +1 à chaque appel
*/
uuid : (function () {
var id = Math.floor(Date.now());
return function () {
return id++;
};
})(),
/**
* Cette fonction réalise l'appel du service fourni via le paramètre "options.url"
* en mettant en œuvre le protocole JSONP.
*
* @method call
* @static
* @param {Object} options - parametres d'invocation du service en JSONP
* @param {String} options.url - URL du service à invoquer (indépendamment du protocole JSONP).
* Cette URL contient déjà les paramètres du service.
* Si le paramètre dédié à la mise en oeuvre du protocole JSONP (callback=xxx) n'est pas présent, il est rajouté par la fonction ;
* sa valeur est déterminée en fonction du paramètre callbackName.
* @param {Number} [options.timeOut = 0] - Nombre de ms au bout duquel on considère que le service n'a pas répondu.
* Une valeur de 0 pour ce paramètre permet de désactiver la gestion du timeOut.
* @param {String} [options.callbackSuffix = null] - Suffixe de la fonction de callback à rajouter sur l'URL.
* Si aucun suffixe n'est spécifié (cas par défaut), on utilisera l'identifiant this.uuid () comme suffixe. Ex: "callback1458574396582 ()"
* @param {String} [options.callbackName = gp.protocol.jsonp] - Valeur du paramètre callback à rajouter sur l'URL.
* Si l'URL fournie contient déjà le paramètre callback, le paramètre callbackName ne sera pas pris en compte.
* La fonction de callback est créée dynamiquement par la fonction JSONP ;
* elle a deux fonctions :
* elle annule la condition de timeOut
* puis appelle la fonction fournie par l'utilisateur via le paramètre onResponse.
* @param {Function} options.onResponse - Nom de la fonction qui sera appelée lors de la réception des résultats du service.
* Ce paramètre sera ignoré si l'URL contient déjà le paramètre callback.
* La fonction de rappel appelée sera alors celle ayant pour nom la valeur de ce paramètre.
* @param {Function} [options.onTimeOut] - Nom de la fonction qui sera appelée en cas de non réponse du service.
* Le temps au bout duquel on considère que le service n'a pas répondu est déterminé par le paramètre timeOut.
* @example
* var options = {
* url : 'http://localhost/some/test.json&callback=myResults',
* timeOut : 100,
* callbackName : 'myResults',
* callbackSuffix : "",
* onResponse : function (response) {
* console.log('results : ', response);
* },
*
* };
* JSONP.call(options);
*/
call : function (options) {
// logger
var logger = Logger.getLogger("JSONP");
logger.trace("[JSONP::call ()]");
// analyse parametres
if (!options) {
logger.error("missing parameter : options !");
throw new Error("missing parameter : options !");
}
if (!options.url) {
logger.error("missing parameter : options.url !");
throw new Error("missing parameter : options.url !");
}
if (!options.timeOut) {
logger.info("setting 'options.timeOut' default value");
options.timeOut = 0;
}
// FIXME si un callback coté client a été mis en place,
// cette condition sur cette methode n'a pas de sens !?
if (!options.onResponse) {
logger.error("missing parameter : options.onResponse !");
throw new Error("missing parameter : options.onResponse !");
// FIXME doit on definir un callback interne par defaut !?
// options.onResponse = function (data) {
// console.log("response callback (inner) : ", data);
// };
}
// ID du callback à utiliser : null ou string.
// si l'utilisateur a spécifié un suffixe pour le callback, on le récupère comme un ID (ex: options.callbackSuffix = "")
// sinon, on utilise un timestamp : this.uuid ()
var callbackId = (typeof options.callbackSuffix === "string") ? options.callbackSuffix : this.uuid();
// on recherche le parametre callback et son nom de fonction dans l'url
var urlHasCallbackKey = false;
var urlHasCallbackName = false;
var idx = options.url.indexOf("callback=");
if (idx !== -1) {
urlHasCallbackKey = true;
// extraction callbackName de l'url : entre "callback=" et "&" ou fin de ligne
var j = options.url.indexOf("&", idx);
if (j === -1) {
j = options.url.length;
}
// on ecrase le parametre options.callbackName s'il avait été défini
var callbackName = options.url.substring(idx + 9, j);
if (callbackName) {
urlHasCallbackName = true;
options.callbackName = callbackName;
logger.info("setting 'options.callbackName' value (" + options.callbackName + ") from 'options.url' parameter");
}
}
// on ajoute le parametre callback dans l'URL s'il n'existe pas
if (!urlHasCallbackKey) {
// gestion des autres param. et "?"
var k = options.url.indexOf("?");
if (k === -1) {
// aucun param., ni de '?'
options.url = options.url + "?" + "callback=";
} else if (k === options.url.length) {
// uniquement le '?'
options.url = options.url + "callback=";
} else {
// le '?' et les param. existent
options.url = options.url + "&" + "callback=";
}
logger.info("setting callback default key in 'options.url' : " + options.url);
}
// utilisation de la fonction callback coté client ?
var HasCallbackName = options.callbackName ? true : urlHasCallbackName;
// on ajoute le nom de la fonction callback dans l'URL si elle n'existe pas
if (!urlHasCallbackName) {
// fonction callback par defaut
if (!options.callbackName) {
logger.info("setting 'options.callbackName' default value");
options.callbackName = "callback"; // ou "gp.protocol.jsonp" ?
// info : si on ne veut pas gerer d'ID dans le callback,
// options.callbackSuffix = ""
if (callbackId || callbackId === "") {
options.callbackName += callbackId;
}
}
options.url = options.url.replace("callback=", "callback=" + options.callbackName);
logger.info("setting callback function name in 'options.url' : " + options.url);
}
// timeOut par defaut
if (!options.onTimeOut) {
logger.info("setting 'options.onTimeOut' default value");
/** callback timeout par defaut */
options.onTimeOut = function (/* error */) {
console.log("TimeOut while invoking url : " + options.url);
};
}
if (!HasCallbackName) {
var self = this;
// event du timeout
var onTimeOutTrigger = null;
// declenche le timeout si > à 0 !
if (options.timeOut > 0) {
onTimeOutTrigger = window.setTimeout(
function () {
/** fonction de reponse du service */
window[options.callbackName] = function () {};
options.onTimeOut();
self._deleteScript(callbackId);
}, options.timeOut);
}
// FIXME le nom de la fonction n'accepte pas de namespace !
// ex. Gp.Function.callback
/**
* fonction de reponse du service
* @param {Object} data - data
* @private
*/
window[options.callbackName] = function (data) {
window.clearTimeout(onTimeOutTrigger);
options.onResponse(data);
self._deleteScript(callbackId);
};
}
this._createScript(callbackId, options.url);
},
/**
* create Script
* @param {String} callbackId - callback Id
* @param {String} url - url
* @private
*/
_createScript : function (callbackId, url) {
var scriptu;
var scripto = document.getElementById("results" + callbackId);
scriptu = document.createElement("script");
scriptu.setAttribute("type", "text/javascript");
scriptu.setAttribute("src", url);
scriptu.setAttribute("charset", "UTF-8");
scriptu.setAttribute("id", "results" + callbackId);
scriptu.setAttribute("async", "true"); // FIXME async ?
// head ou body ou autres ?
var node = document.documentElement || document.getElementsByTagName("head")[0];
if (scripto === null) {
node.appendChild(scriptu);
} else {
// s'il existe déjà, on le remplace !
node.replaceChild(scriptu, scripto);
}
},
/**
* delete Script
* @param {String} callbackId - callback Id
* @private
*/
_deleteScript : function (callbackId) {
var script = document.getElementById("results" + callbackId);
if (script) {
var node = script.parentNode || document.documentElement;
if (!node) {
return;
}
node.removeChild(script);
}
}
};
export default JSONP;