uhm, don't know if it'll help you, BTW, I also had to figure a "way"
to display some "loading pictures" (from now on, I'll call them
"spinners") to my pages.
I had to face 2 distinct problems:
- loading components via Ajax (through LOAD() or web2py_component())
- prevent FOUC on very complex pages

for the first problem I actually "hacked" web2py.js (web2py_ajax.js or
so).
the ajax function needs to be modified....
the basic idea is to replace content of the target with a spinner (in
this case, a div containing the message "loading...") before sending
the request, and upon success replace it with the content actually
loaded...
you can add a beforeSend parameter for that and take care of replacing
the content adding something to the success function.

My code actually is something like this...

function ajax(u,s,t) {
    query = '';
    if (typeof s == "string") {
        d = jQuery(s).serialize();
        if(d){ query = d; }
    } else {
        pcs = [];
        if (s != null && s != undefined) for(i=0; i<s.length; i++) {
            q = jQuery("[name="+s[i]+"]").serialize();
            if(q){pcs.push(q);}
        }
        if (pcs.length>0){query = pcs.join("&");}
    }
    jQuery.ajax({
        type: "POST",
        url: u,
        data: query,
        beforeSend: function( xhr ) {
                    if(t) {jQuery("#" + t).html('<div
id="loadingwindow">Loading...</div>')};
                },
        success: function(msg) {
            if(t) {
                if(t==':eval') eval(msg);
                else {
                        jQuery("#loadingwindow").fadeOut(400, function
() {
                        jQuery("#" + t).html(msg).hide().fadeIn(400);
                        });
                    }
                }
        }
    });
}

for the second problem, I had to hide window contents because of some
complex javascript needs to be run to validate several fields and to
rearrange heavily the DOM (tabs, selects with a large number of
options to turn into multiselects, a tree with jstree) until the
"transformation" completes.
Fortunately I could figure out what was the "script" block that is
going to be always the last to be finished (not executed), so I can
address that "event" with the "action" of displaying my page again.
All this introduction to warn you about an hiccup in the following
method: if you have built (like me) some widgets to be modular, you'll
end up having various <script> blocks around your page: from what I
can tell you can only be sure that they are executed "in order" of
what is found in the page, but you can't tell what will be the last to
stop executing. NB: this method works only if you must display a
spinner in a one-time-only manner, e.g.: you have a complex page that
needs to be temporarily "hidden" and you want to prevent FOUC.

Truth told, I figured out this way to prevent FOUC and display the
page after all DOM work is done :

step 1:
add to the css this rule

#wloading.amloading {
        position: absolute;
    left: -10000px;
}

step 2: add a function to web2py.js (or anywhere else that you are
sure it's loaded with every page)

function finished_loading() {
        if ($('loadingwindow').length == 0) {
                setTimeout(function() {
                $("#wloading").removeClass("amloading");
                $("#loadingwindow").remove();
            }, 1500);
   }
}

step 3:
define two "widgets" in models (can be elaborated further with
positioning, simulating "modal" loading, etc etc etc.). Following is
the basic ones

def loading_widget_start():
        script = """
        $("#wloading").addClass("amloading");
        $("#wloading").before('<div id="loadingwindow">Loading...</div>');
        """
        rtn = SCRIPT(script, _type="text/javascript")
        return rtn

def loading_widget_stop():
        script = """
        setTimeout(function() {
                finished_loading();
            }, 5000);
        """
        rtn = SCRIPT(script, _type="text/javascript")
        return rtn

step 4:
embed every page that needs to be loaded in a <div id="wloading">

step 5:
add {{=loading_widget_start()}} right after <div id="wloading"> and
before the actual content

step 6:
put {{=loading_widget_stop()}} at the very end of the page (just to be
sure)

You can now use the finished_loading() "callback" after the most
expensive <script> or complex functions and be sure that the content
will be hidden until callback is called. If you don't call
finished_loading() explicitely, the loading_widget_stop() will be
called at the very end of your page and will wait 5 seconds before
showing the page to the user.

If you have further questions don't be afraid to ask.

PS: I'm aware that I'm not using a document.ready event to fire all
up, that kind of "blocks" the whole idea of using a function before
all DOM is loaded.....from what I can tell as long as jquery is loaded
in the <head> you can be sure that the library is loaded before the
body, so working on a div that is declared just before the "managing
it with jquery" <script> will work.

I'm not a javascript guru, I'm just compelled to have a page working
the way I (or my users) want in the shortest time possible.

If you know a better and "traditional" method please tell me, I'm
eager to learn and maybe you'll help others in the same "condition" :D

Reply via email to