jQuery.spip=jQuery.spip || {}; jQuery.spip.log = function(){ if (jQuery.spip.debug && window.console && window.console.log) window.console.log.apply(this,arguments); } // A plugin that wraps all ajax calls introducing a fixed callback function on ajax complete if(!jQuery.spip.load_handlers) { jQuery.spip.load_handlers = new Array(); /** * OnAjaxLoad allow to * add a function to the list of those * to be executed on ajax load complete * * most of time function f is applied on the loaded data * if not known, the whole document is targetted * * @param function f */ function onAjaxLoad(f) { jQuery.spip.load_handlers.push(f); }; /** * Call the functions that have been added to onAjaxLoad * @param root */ jQuery.spip.triggerAjaxLoad = function (root) { jQuery.spip.log('triggerAjaxLoad'); jQuery.spip.log(root); for ( var i = 0; i < jQuery.spip.load_handlers.length; i++ ) jQuery.spip.load_handlers[i].apply( root ); }; jQuery.spip.intercepted={}; // intercept jQuery.fn.load jQuery.spip.intercepted.load = jQuery.fn.load; jQuery.fn.load = function( url, params, callback ) { if ( typeof url !== "string") { return jQuery.spip.intercepted.load.apply( this, arguments ); } callback = callback || function(){}; // If the second parameter was provided if ( params ) { // If it's a function if ( params.constructor == Function ) { // We assume that it's the callback callback = params; params = null; } } params = jQuery.extend(params, {triggerAjaxLoad:false}); // prevent $.ajax to triggerAjaxLoad var callback2 = function() {jQuery.spip.log('jQuery.load');jQuery.spip.triggerAjaxLoad(this);callback.apply(this,arguments);}; return jQuery.spip.intercepted.load.apply(this,[url, params, callback2]); }; // intercept jQuery.fn.ajaxSubmit jQuery.spip.intercepted.ajaxSubmit = jQuery.fn.ajaxSubmit; jQuery.fn.ajaxSubmit = function(options){ // find the first parent that will not be removed by formulaire_dyn_ajax // or take the whole document options = options || {}; if (typeof options.onAjaxLoad=="undefined" || options.onAjaxLoad!=false) { var me=jQuery(this).parents('div.ajax'); if (me.length) me=me.parent(); else me = document; if (typeof options=='function') options = { success: options }; var callback = options.success || function(){}; options.success = function(){callback.apply(this,arguments);jQuery.spip.log('jQuery.ajaxSubmit');jQuery.spip.triggerAjaxLoad(me);} } return jQuery.spip.intercepted.ajaxSubmit.apply(this,[options]); } // intercept jQuery.ajax jQuery.spip.intercepted.ajax = jQuery.ajax; jQuery.ajax = function(url, settings) { if (typeof settings == 'undefined') { settings = {}; if (typeof url == 'object') { settings = url; url = null; } } if (typeof url == 'string') { settings['url'] = url; } // if triggerAjaxLoad is prevented, finish without faking callback if (settings.data && settings.data['triggerAjaxLoad'] === false) { settings.data['triggerAjaxLoad'] = null; return jQuery.spip.intercepted.ajax(settings); } var s = jQuery.extend(true, {}, jQuery.ajaxSettings, settings); var callbackContext = s.context || s; try { if (jQuery.ajax.caller==jQuery.spip.intercepted.load || jQuery.ajax.caller==jQuery.spip.intercepted.ajaxSubmit) return jQuery.spip.intercepted.ajax(settings); } catch (err){} var orig_complete = s.complete || function() {}; settings.complete = function(res,status) { // Do not fire OnAjaxLoad if the dataType is not html var dataType = settings.dataType; var ct = (res && (typeof res.getResponseHeader == 'function')) ? res.getResponseHeader("content-type"): ''; var xml = !dataType && ct && ct.indexOf("xml") >= 0; orig_complete.call( callbackContext, res, status); if((!dataType && !xml) || dataType == "html") { jQuery.spip.log('jQuery.ajax'); if (typeof s.onAjaxLoad=="undefined" || s.onAjaxLoad!=false) jQuery.spip.triggerAjaxLoad(s.ajaxTarget?s.ajaxTarget:document); } }; return jQuery.spip.intercepted.ajax(settings); }; } /* jQuery.browser */ jQuery.uaMatch = function( ua ) { ua = ua.toLowerCase(); var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || /(webkit)[ \/]([\w.]+)/.exec( ua ) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || /(msie) ([\w.]+)/.exec( ua ) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || []; return { browser: match[ 1 ] || "", version: match[ 2 ] || "0" }; }; // Don't clobber any existing jQuery.browser in case it's different if ( !jQuery.browser ) { matched = jQuery.uaMatch( navigator.userAgent ); browser = {}; if ( matched.browser ) { browser[ matched.browser ] = true; browser.version = matched.version; } // Chrome is Webkit, but Webkit is also Safari. if ( browser.chrome ) { browser.webkit = true; } else if ( browser.webkit ) { browser.safari = true; } jQuery.browser = browser; } // jQuery.getScript cache par defaut jQuery.getScript = function(url,callback){ return $.ajax({ url: url, dataType: "script", success: callback, cache: true }); } /** * if not fully visible, scroll the page to position * target block at the top of page * if force = true, allways scroll * * @param bool force */ jQuery.fn.positionner = function(force, setfocus) { var offset = jQuery(this).offset(); var hauteur = parseInt(jQuery(this).css('height')); var scrolltop = self['pageYOffset'] || jQuery.boxModel && document.documentElement[ 'scrollTop' ] || document.body[ 'scrollTop' ]; var h = jQuery(window).height(); var scroll=0; if (force || (offset && offset['top'] - 5 <= scrolltop)) scroll = offset['top'] - 5; else if (offset && offset['top'] + hauteur - h + 5 > scrolltop) scroll = Math.min(offset['top'] - 5, offset['top'] + hauteur - h + 15); if (scroll) jQuery('html,body') .animate({scrollTop: scroll}, 300); // positionner le curseur dans la premiere zone de saisie if (setfocus!==false) jQuery(jQuery('*', this).filter('input[type=text],textarea')[0]).focus(); return this; // don't break the chain } // deux fonctions pour rendre l'ajax compatible Jaws jQuery.spip.virtualbuffer_id='spip_virtualbufferupdate'; jQuery.spip.initReaderBuffer = function(){ if (jQuery('#'+jQuery.spip.virtualbuffer_id).length) return; jQuery('body').append('
'); } jQuery.spip.updateReaderBuffer = function(){ var i = jQuery('#'+jQuery.spip.virtualbuffer_id); if (!i.length) return; // incrementons l'input hidden, ce qui a pour effet de forcer le rafraichissement du // buffer du lecteur d'ecran (au moins dans Jaws) i.val(parseInt(i.val())+1); } jQuery.fn.formulaire_setARIA = function(){ if (!this.closest('.ariaformprop').length){ // eviter une double execution du js au moment de sa reinsertion dans le DOM par wrap() // cf http://bugs.jquery.com/ticket/7447 this.find('script').remove(); this.wrap(''); // dans un formulaire, le screen reader relit tout a chaque saisie d'un caractere si on est en aria-live jQuery('form',this).not('[aria-live]').attr('aria-live','off'); } return this; } /** * rechargement ajax d'un formulaire dynamique implemente par formulaires/xxx.html * @param target */ jQuery.fn.formulaire_dyn_ajax = function(target) { if (this.length) jQuery.spip.initReaderBuffer(); return this.each(function() { var scrollwhensubmit = !jQuery(this).is('.noscroll'); var cible = target || this; jQuery(cible).formulaire_setARIA(); jQuery('form:not(.noajax):not(.bouton_action_post)', this).each(function(){ var leform = this; var leclk,leclk_x,leclk_y; var onError = function(xhr, status, error, $form){ jQuery(leform).ajaxFormUnbind().find('input[name="var_ajax"]').remove(); var msg = "Erreur"; if (typeof(error_on_ajaxform)!=="undefined") msg = error_on_ajaxform; jQuery(leform).prepend(""+msg+"
").find('.ajax-error').show('fast'); jQuery(cible).closest('.ariaformprop').endLoading(true); } jQuery(this).prepend("") .ajaxForm({ beforeSubmit: function(){ // memoriser le bouton clique, en cas de repost non ajax leclk = leform.clk; var scrollwhensubmit_button = true; if (leclk) { scrollwhensubmit_button = !jQuery(leclk).is('.noscroll'); var n = leclk.name; if (n && !leclk.disabled && leclk.type == "image") { leclk_x = leform.clk_x; leclk_y = leform.clk_y; } } jQuery(cible).wrap(''); cible = jQuery(cible).parent(); jQuery(cible).closest('.ariaformprop').animateLoading(); if (scrollwhensubmit && scrollwhensubmit_button) { jQuery(cible).positionner(false,false); } }, error: onError, success: function(c, status, xhr , $form){ if (c.match(/^\s*noajax\s*$/)){ // le serveur ne veut pas traiter ce formulaire en ajax // on resubmit sans ajax jQuery("input[name=var_ajax]",leform).remove(); // si on a memorise le nom et la valeur du bouton clique // les reinjecter dans le dom sous forme de input hidden // pour que le serveur les recoive if (leclk){ var n = leclk.name; if (n && !leclk.disabled) { jQuery(leform).prepend(""); if (leclk.type == "image") { jQuery(leform).prepend(""); jQuery(leform).prepend(""); } } } jQuery(leform).ajaxFormUnbind().submit(); } else { if (!c.length || c.indexOf("ajax-form-is-ok")==-1) return onError.apply(this,[status, xhr , $form]); // commencons par vider le cache des urls, si jamais un js au retour // essaye tout de suite de suivre un lien en cache // dans le doute sur la validite du cache il vaut mieux l'invalider var preloaded = jQuery.spip.preloaded_urls; jQuery.spip.preloaded_urls = {}; jQuery(cible).html(c); var a = jQuery('a:first',cible).eq(0); var d = jQuery('div.ajax',cible); if (!d.length){ // si pas .ajax dans le form, remettre la classe sur le div que l'on a insere jQuery(cible).addClass('ajax'); if (!scrollwhensubmit) jQuery(cible).addClass('noscroll'); } else { // sinon nettoyer les br ajaxie d.siblings('br.bugajaxie').remove(); // desemboiter d'un niveau pour manger le div que l'on a insere cible = jQuery(":first",cible); cible.unwrap(); } // chercher une ancre en debut de html pour positionner la page if (a.length && a.is('a[name=ajax_ancre]') && jQuery(a.attr('href'),cible).length){ a = a.attr('href'); if (jQuery(a,cible).length) setTimeout(function(){ jQuery(a,cible).positionner(false); //a = a.split('#'); //window.location.hash = a[1]; },10); jQuery(cible).closest('.ariaformprop').endLoading(true); } else{ //jQuery(cible).positionner(false); if (a.length && a.is('a[name=ajax_redirect]')){ a = a.get(0).href; setTimeout(function(){ var cur = window.location.href.split('#'); document.location.replace(a); // regarder si c'est juste un changement d'ancre : dans ce cas il faut reload // (le faire systematiquement provoque des bugs) if (cur[0]==a.split('#')[0]){ window.location.reload(); } },10); // ne pas arreter l'etat loading, puisqu'on redirige ! // mais le relancer car l'image loading a pu disparaitre jQuery(cible).closest('.ariaformprop').animateLoading(); } else { jQuery(cible).closest('.ariaformprop').endLoading(true); } } // si jamais le formulaire n'a pas un retour OK, retablissons le cache // car a priori on a pas fait d'operation en base de donnee if (!jQuery('.reponse_formulaire_ok',cible).length) jQuery.spip.preloaded_urls = preloaded; // mettre a jour le buffer du navigateur pour aider jaws et autres readers // a supprimer ? jQuery.spip.updateReaderBuffer(); } }/*, iframe: jQuery.browser.msie*/ }) // previent qu'on n'ajaxera pas deux fois le meme formulaire en cas de ajaxload // mais le marquer comme ayant l'ajax au cas ou on reinjecte du contenu ajax dedans .addClass('noajax hasajax'); }); }); } jQuery.fn.formulaire_verifier = function(callback, champ){ var erreurs = {'message_erreur':'form non ajax'}; var me=this; // si on est aussi en train de submit pour de vrai, abandonner if (jQuery(me).closest('.ariaformprop').attr('aria-busy')!='true') { if (jQuery(me).is('form.hasajax')){ jQuery(me).ajaxSubmit({ dataType:"json", data:{formulaire_action_verifier_json:true}, success:function(errs){ var args = [errs, champ] // si on est aussi en train de submit pour de vrai, abandonner if (jQuery(me).closest('.ariaformprop').attr('aria-busy')!='true') callback.apply(me,args); } }); } else callback.apply(me,[erreurs, champ]); } return this; } jQuery.fn.formulaire_activer_verif_auto = function(callback){ callback = callback || formulaire_actualiser_erreurs; var me = jQuery(this).closest('.ariaformprop'); var check = function(){ var name=jQuery(this).attr('name'); // declencher apres 50ms pour ne pas double submit sur sequence saisie+submit setTimeout(function(){me.find('form').formulaire_verifier(callback,name);},50); } var activer = function(){ if (me.find('form').attr('data-verifjson')!='on'){ me .find('form') .attr('data-verifjson','on') .find('input,select,textarea') .on('change', check); } } jQuery(activer); onAjaxLoad(function(){setTimeout(activer,150);}); } function formulaire_actualiser_erreurs(erreurs){ var parent = jQuery(this).closest('.formulaire_spip'); if (!parent.length) return; // d'abord effacer tous les messages d'erreurs parent.find('.reponse_formulaire,.erreur_message').fadeOut().remove(); parent.find('.erreur').removeClass('erreur'); // ensuite afficher les nouveaux messages d'erreur if (erreurs['message_ok']) parent.find('form').before(''+erreurs['message_ok']+'
'); if (erreurs['message_erreur']) parent.find('form').before(''+erreurs['message_erreur']+'
'); for (var k in erreurs){ var saisie = parent.find('.editer_'+k); if (saisie.length) { saisie.addClass('erreur'); saisie.find('label').after(' '); } } } // permettre d'utiliser onclick='return confirm('etes vous sur?');' sur un lien ajax var ajax_confirm=true; var ajax_confirm_date=0; var spip_confirm = window.confirm; function _confirm(message){ ajax_confirm = spip_confirm(message); if (!ajax_confirm) { var d = new Date(); ajax_confirm_date = d.getTime(); } return ajax_confirm; } window.confirm = _confirm; /** * rechargement ajax d'une noisette implementee par {ajax} * selecteur personalise, sera defini par defaut a '.pagination a,a.ajax' */ var ajaxbloc_selecteur; /** * mise en cache des url. Il suffit de vider cete variable pour vider le cache */ jQuery.spip.preloaded_urls = {}; /** * Afficher dans la page * le html d'un bloc ajax charge * @param object blocfrag * @param string c * @param string href * @param bool history */ jQuery.spip.on_ajax_loaded = function(blocfrag,c,href,history) { history = history || (history==null); if (typeof href == undefined || href==null) history = false; if (history) jQuery.spip.setHistoryState(blocfrag); if (jQuery(blocfrag).attr('data-loaded-callback')){ var callback = eval(jQuery(blocfrag).attr('data-loaded-callback')); callback.call(blocfrag, c, href, history); } else { jQuery(blocfrag) .html(c) .endLoading(); } if (typeof href != undefined) jQuery(blocfrag).attr('data-url',href); if (history) { jQuery.spip.pushHistoryState(href); jQuery.spip.setHistoryState(blocfrag); } var a = jQuery('a:first',jQuery(blocfrag)).eq(0); if (a.length && a.is('a[name=ajax_ancre]') && jQuery(a.attr('href'),blocfrag).length){ a = a.attr('href'); jQuery(a,blocfrag).positionner(false); } jQuery.spip.log('on_ajax_loaded'); jQuery.spip.triggerAjaxLoad(blocfrag); // si le fragment ajax est dans un form ajax, // il faut remettre a jour les evenements attaches // car le fragment peut comporter des submit ou button a = jQuery(blocfrag).parents('form.hasajax') if (a.length) a.eq(0).removeClass('noajax').parents('div.ajax').formulaire_dyn_ajax(); jQuery.spip.updateReaderBuffer(); } jQuery.spip.on_ajax_failed = function(blocfrag,statusCode,href,history) { // marquer le bloc invalide jQuery(blocfrag).addClass('invalid'); // si c'est une erreur 400 on a perdu la signature ajax //console.log("AJAX Erreur"); //console.log(statusCode); history = history || (history==null); // quelle que soit l'erreur, on redirige si c'était la nouvelle URL principale de la page if (history) { //console.log("On redirige : " + href); window.location.href = href; } } jQuery.spip.stateId=0; jQuery.spip.setHistoryState = function(blocfrag){ if (!window.history.replaceState) return; // attribuer un id au bloc si il n'en a pas if (!blocfrag.attr('id')){ while (jQuery('#ghsid'+jQuery.spip.stateId).length) jQuery.spip.stateId++; blocfrag.attr('id','ghsid'+jQuery.spip.stateId); } var href= blocfrag.attr('data-url') || blocfrag.attr('data-origin'); href = jQuery("<"+"a href='"+href+"'>").get(0).href; var state={ id:blocfrag.attr('id'), href: href }; var ajaxid = blocfrag.attr('class').match(/\bajax-id-[\w-]+\b/); if (ajaxid && ajaxid.length) state["ajaxid"] = ajaxid[0]; // on remplace la variable qui decrit l'etat courant // initialement vide // -> elle servira a revenir dans l'etat courant window.history.replaceState(state,window.document.title, window.document.location); } jQuery.spip.pushHistoryState = function(href, title){ if (!window.history.pushState) return false; window.history.pushState({}, title, href); } window.onpopstate = function(popState){ if (popState.state && popState.state.href){ var blocfrag=false; if (popState.state.id){ blocfrag=jQuery('#'+popState.state.id); } if ((!blocfrag || !blocfrag.length) && popState.state.ajaxid){ blocfrag=jQuery('.ajaxbloc.'+popState.state.ajaxid); } if (blocfrag && blocfrag.length==1) { jQuery.spip.ajaxClick(blocfrag,popState.state.href,{history:false}); return true; } // si on revient apres avoir rompu la chaine ajax, on a pu perdre l'id #ghsidxx ajoute en JS // dans ce cas on redirige hors ajax else { window.location.href = popState.state.href; } } } /** * Charger un bloc ajax represente par l'objet jQuery blocajax qui le pointe * avec la requete ajax url, qui represente le lien href * @param object blocfrag * bloc cible * @param string url * url pour la requete ajax * @param string href * url du lien clique * @param object options * bool force : pour forcer la requete sans utiliser le cache * function callback : callback au retour du chargement * bool history : prendre en charge l'histrisation dans l'url */ jQuery.spip.loadAjax = function(blocfrag,url, href, options){ var force = options.force || false; if (jQuery(blocfrag).attr('data-loading-callback')){ var callback = eval(jQuery(blocfrag).attr('data-loading-callback')); callback.call(blocfrag,url,href,options); } else { jQuery(blocfrag).animateLoading(); } if (jQuery.spip.preloaded_urls[url] && !force) { // si on est deja en train de charger ce fragment, revenir plus tard if (jQuery.spip.preloaded_urls[url]==""){ setTimeout(function(){jQuery.spip.loadAjax(blocfrag,url,href,options);},100); return; } jQuery.spip.on_ajax_loaded(blocfrag,jQuery.spip.preloaded_urls[url],href,options.history); } else { var d = new Date(); jQuery.spip.preloaded_urls[url] = ""; jQuery.ajax({ url: parametre_url(url,'var_t',d.getTime()), onAjaxLoad:false, success: function(c){ jQuery.spip.on_ajax_loaded(blocfrag,c,href,options.history); jQuery.spip.preloaded_urls[url] = c; if (options.callback && typeof options.callback == "function") options.callback.apply(blocfrag); }, error: function(e){ jQuery.spip.preloaded_urls[url]=''; jQuery.spip.on_ajax_failed(blocfrag,e.status,href,options.history); } }); } } /** * Calculer l'url ajax a partir de l'url du lien * et de la variable d'environnement du bloc ajax * passe aussi l'ancre eventuelle sous forme d'une variable * pour que le serveur puisse la prendre en compte * et la propager jusqu'a la reponse * sous la forme d'un lien cache * * @param string href * @param string ajax_env */ jQuery.spip.makeAjaxUrl = function(href,ajax_env,origin){ var url = href.split('#'); url[0] = parametre_url(url[0],'var_ajax',1); url[0] = parametre_url(url[0],'var_ajax_env',ajax_env); // les arguments de origin qui ne sont pas dans href doivent etre explicitement fournis vides dans url if (origin){ var p=origin.indexOf('?'); if (p!==-1){ // recuperer la base var args = origin.substring(p+1).split('&'); var val; var arg; for(var n=0;n