All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.primefaces.ajaxstatus.ajaxstatus.js Maven / Gradle / Ivy

There is a newer version: 7.0
Show newest version
/**
 * __PrimeFaces AjaxStatus Widget__
 * 
 * AjaxStatus is a global notifier for AJAX requests.
 * 
 * For the callbacks that can be set via the `onstart`, `onsuccess`, `onerror` and `oncomplete` attributes, see
 * {@link PfAjaxStartCallback}, {@link PfAjaxSuccessCallback}, {@link PfAjaxErrorCallback}, and
 * {@link PfAjaxCompleteCallback}.
 * 
 * @typedef {"start" | "success" | "error" | "complete"} PrimeFaces.widget.AjaxStatus.AjaxStatusEventType Available
 * types of AJAX related events to which you can listen.
 * 
 * @typedef PrimeFaces.widget.AjaxStatus.PfAjaxStartCallback Callback for when an AJAX request starts. Usually set via
 * ``. This callback applies when `` is used.
 * @this {Document} PrimeFaces.widget.AjaxStatus.PfAjaxStartCallback
 * 
 * @typedef PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback Callback for when an AJAX request fails. Usually set via
 * ``. This callback applies when `` is used.
 * @this {Document} PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback
 * @param {JQuery.jqXHR} PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback.xhr The request that failed.
 * @param {JQuery.AjaxSettings} PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback.settings The settings of the jQuery
 * AJAX request.
 * @param {string} PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback.errorThrown The error that cause the request to
 * fail.

 * @typedef PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback Callback for when an AJAX request succeeds. Usually set
 * via ``. This callback applies when `` is used.
 * @this {Document} PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback
 * @param {JQuery.jqXHR} PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback.xhr The request that succeeded.
 * @param {JQuery.AjaxSettings} PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback.settings The settings of the jQuery
 * AJAX request.
 * 
 * @typedef PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback Callback for when an AJAX request completes, either
 * successfully or with an error. Usually set via ``. This callback applies when
 * `` is used.
 * @this {Document} PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback
 * @param {JQuery.jqXHR} PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback.xhr The request that succeeded.
 * @param {JQuery.AjaxSettings} PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback.settings The settings of the jQuery
 * AJAX request.
 * 
 * @interface {PrimeFaces.widget.AjaxStatus.EventToCallbackMap} EventToCallbackMap Maps between the
 * {@link AjaxStatusEventType} and the corresponding event handlers. Used by the {@link AjaxStatus} component.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback | jsf.ajax.OnEventCallback | jsf.ajax.OnErrorCallback} EventToCallbackMap.complete
 * Callback for when an AJAX request completes, either successfully or with an error. Usually set via
 * ``.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback | jsf.ajax.OnErrorCallback} EventToCallbackMap.error Callback
 * for when an AJAX request fails. Usually set via ``.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxStartCallback | jsf.ajax.OnEventCallback} EventToCallbackMap.start Callback
 * for when an AJAX request starts. Usually set via ``.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback | jsf.ajax.OnEventCallback} EventToCallbackMap.success
 * Callback for when an AJAX request succeeds. Usually set via ``.
 * 
 * @prop {number | null} timeout The set-timeout timer ID for the timer of the delay before the AJAX status is
 * triggered.
 * @prop {boolean} hasSuccessOrErrorFacet True if this component contains a success/error facet.
 * 
 * @interface {PrimeFaces.widget.AjaxStatusCfg} cfg The configuration for the {@link  AjaxStatus| AjaxStatus widget}.
 * You can access this configuration via {@link PrimeFaces.widget.BaseWidget.cfg|BaseWidget.cfg}. Please note that this
 * configuration is usually meant to be read-only and should not be modified.
 * @extends {PrimeFaces.widget.BaseWidgetCfg} cfg
 * 
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxCompleteCallback | jsf.ajax.OnEventCallback | jsf.ajax.OnErrorCallback} cfg.complete
 * Client-side callback for when the AJAX behavior completes, i.e. when the request finishes, irrespective of whether it
 * succeeded or failed. 
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxErrorCallback | jsf.ajax.OnErrorCallback} cfg.error Client-side callback
 * for when the AJAX behavior fails, i.e. when the request fails.
 * @prop {number} cfg.delay Delay in milliseconds before displaying the AJAX status. Default is `0`, meaning immediate.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxStartCallback | jsf.ajax.OnEventCallback} cfg.start Client-side callback
 * for when the AJAX behavior starts, i.e. the request is about to be sent.
 * @prop {PrimeFaces.widget.AjaxStatus.PfAjaxSuccessCallback | jsf.ajax.OnEventCallback} cfg.success Client-side
 * callback for when the AJAX  behavior completes successfully, i.e. when the request succeeds.
 */
PrimeFaces.widget.AjaxStatus = PrimeFaces.widget.BaseWidget.extend({

    /**
     * @override
     * @inheritdoc
     * @param {PrimeFaces.PartialWidgetCfg} cfg
     */
    init: function(cfg) {
        this._super(cfg);
        this.hasSuccessOrErrorFacet = false;

        this.bind();
    },

    /**
     * Binds event handlers to AJAX events on the document element.
     * Handles both PrimeFaces AJAX events and native JSF AJAX events.
     * 
     * For PrimeFaces AJAX events:
     * - pfAjaxStart: Triggered when AJAX request starts. After configured delay, triggers 'start' event.
     * - pfAjaxError: Triggered when AJAX request fails. Triggers 'error' event with xhr, settings, error details.
     * - pfAjaxSuccess: Triggered when AJAX request succeeds. Triggers 'success' event with xhr, settings.
     * - pfAjaxComplete: Triggered after success/error. Clears timeout if no redirect. Triggers 'complete' event.
     * 
     * For JSF AJAX events:
     * - begin: Triggers 'start' event after configured delay
     * - complete: IGNORED since it fires before success/error
     * - success: Clears timeout, triggers 'success' then 'complete' events
     * - error: Logs error, clears timeout, triggers 'error' then 'complete' events
     * 
     * Events are namespaced with component ID for cleanup.
     * Configured delay controls timing of showing AJAX status.
     * Cleanup is handled via destroy listener that removes document event handlers.
     * @private
     */
    bind: function() {
        var $this = this;
        var namespace = '.status' + this.id;

        // Handle start of AJAX request
        $(document).on('pfAjaxStart' + namespace, function() {
            // Queue task to trigger start event after configured delay
            $this.timeout = PrimeFaces.queueTask(function() {
                $this.trigger('start', arguments);
            }, $this.cfg.delay);
        })
        // Handle AJAX error
        .on('pfAjaxError' + namespace, function(e, xhr, settings, error) {
            $this.trigger('error', [xhr, settings, error]);
        })
        // Handle AJAX success
        .on('pfAjaxSuccess' + namespace, function(e, xhr, settings) {
            $this.trigger('success', [xhr, settings]);
        })
        // Handle AJAX completion (after success/error)
        .on('pfAjaxComplete' + namespace, function(e, xhr, settings, args) {
            if($this.timeout && args && !args.redirect) {
                $this.deleteTimeout();
            }
            $this.trigger('complete', [xhr, settings, args]);
        });
        this.addDestroyListener(function() {
            $(document).off(namespace);
        });

        // also bind to Faces (f:ajax) events
        // NOTE: PrimeFaces fires "complete" as the final event, while Faces ends with either "success" or "error", 
        // requiring us to manually trigger a "complete" event in those cases
        if (window.jsf && jsf.ajax) {
            jsf.ajax.addOnEvent(function(data) {
                if(data.status === 'begin') {

                    $this.timeout = PrimeFaces.queueTask(function() {
                        $this.trigger('start', arguments);
                    }, $this.cfg.delay);
                }
                else if(data.status === 'complete') {
                    // Ignore JSF complete event since it fires before success/error. We'll trigger complete manually after success/error to match PrimeFaces event order
                }
                else if(data.status === 'success') {
                    $this.deleteTimeout();
                    $this.trigger('success', arguments);
                    $this.trigger('complete', arguments);
                }
            });

            jsf.ajax.addOnError(function(data) {
                PrimeFaces.error(data);
                $this.deleteTimeout();
                $this.trigger('error', arguments);
                $this.trigger('complete', arguments);
            });
        }
    },

    /**
     * Triggers the given event by invoking the event handler, usually defined on the `` tag.
     * @template {PrimeFaces.widget.AjaxStatus.AjaxStatusEventType} K A name of one of the supported events that should
     * be triggered.
     * @param {K} event A name of one of the supported events that should
     * be triggered.
     * @param {Parameters} args Arguments that are passed to the
     * event handler.
     */
    trigger: function(event, args) {
        var callback = this.cfg[event];
        if (callback) {
            callback.apply(document, args);
        }

        // Get the facet based on the event
        var facets = this.jq.children();
        var facet = facets.filter(this.toFacetId(event));
        var hasFacet = facet && facet.length > 0;

        // We have the following events:
        // 1) start
        // 2) success or error
        // 3) complete
        switch (event) {
            case 'start':
                // always hide other facets on start
                facets.hide();

                if (hasFacet) {
                    facet.show();
                }
                break;

            case 'success':
            case 'error':
                // we now expect that either a complete or success/error facet is defined
                // if no success/error is defined, lets just rely upon the complete-facet
                if (hasFacet) {
                    facets.hide();
                    facet.show();
                    this.hasSuccessOrErrorFacet = true;
                }
                break;

            case 'complete':
                // Skip hiding the previous facet (typically the start facet) if the request results in a redirect
                // Note: This won't work properly with success/error facets since redirect info isn't available beforehand
                // Only check for redirect if PrimeFaces is used
                if (args.length > 1) { // PrimeFaces passes 3 args, JSF passes 1 arg
                    var pfArgs = args[2];
                    if (!pfArgs || pfArgs.redirect) {
                        return;
                    }
                }

                // #11824 hide the start facet if there was no error/success facet or there is a complete facet
                if (this.hasSuccessOrErrorFacet === false || hasFacet) {
                    facets.hide();
                }
                // Show complete-facet if defined
                if (hasFacet) {
                    facet.show();
                }
                break;
        }
    },

    /**
     * Finds the facet ID of the given event.
     * @private
     * @param {PrimeFaces.widget.AjaxStatus.AjaxStatusEventType} event One of the supported event
     * @return {string} The ID of the facet element for the given event
     */
    toFacetId: function(event) {
        return this.jqId + '_' + event;
    },

    /**
     * Clears the ste-timeout timer for the delay.
     * @private
     */
    deleteTimeout: function() {
        if (this.timeout) {
            clearTimeout(this.timeout);
            this.timeout = null;
        }
    }

});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy