Menu

#462 catch clicks while page is loading

workingwiki
open
None
5
2014-03-20
2014-01-18
Anonymous
No

Because MW speeds the page load along by late-loading as much of the JS and CSS as possible, and I'm going along with that design, we load most of the WW javascript code at the end of the page load. As a consequence, if you click a WW link, say to create or destroy a background job, if the page hasn't finished loading you get the no-javascript fallback implementation of the link: it takes you away to a ManageProject page that does the operation before it loads.

I'd like to put in a minimal bit of JS code to intercept those clicks and make them do the intended thing, which is almost surely to do the operation inline just as they do after the page is done loading.

Discussion

  • Lee Worden

    Lee Worden - 2014-01-18

    I started work on an implementation where it slaps a spinner on the link when you click it, and puts it on a queue to get dealt with later when the code arrives.

    But then I was thinking, if I click a link while the page is loading, it'll often be because I want to do the operation now, without waiting. So now I'm thinking about an implementation that starts loading the necessary code right away, regardless of whether it slows down the overall page load. To do this I guess I would package up the minimal code you need to create and send a WW API request, so I could load and use that when there's a click. The code that's needed to deal with the result of the API request, on the other hand, I could load after sending the request...

    So minimally I need:

    ww.injectSpinner : immediately - load before the HTML, because spinner needs to appear with no delay
    ww.api : load quickly when link is clicked
    ww.confirm : load quickly when link is clicked (if needed).
    ww.notify : load after API request is sent

    Or if api and confirm (and whatever apparatus they need) are small enough I could just top-load them along with injectSpinner.

     
  • Lee Worden

    Lee Worden - 2014-01-22

    It's nice to set up the links after they load, with jquery, because you can have various data in the function scope. But that's hopeless for this, because we need to not have a race condition when a link is created and doesn't yet have the right behavior. So I think the only way is to ship each <a> tag in the html with an onclick="doSomething()" attribute whose doSomething() string is as compact as possible.

    Also we want the top-loaded JS resources to be as compact as possible, which means not defining 27 different onclick functions for all the different kinds of links.

    I think the only sensible design is onclick="wwlink({action:'ww-create-background-job',project='Sandbox',target='test-mk.out'})". Even though it's not very compact and in some cases providing the full API data like that will be redundant, as it is stored nearby in the DOM and could be retrieved from there (I do it that way now). In other words, this will provide a good response when the page hasn't fully loaded, at the cost of the time it takes to fully load.

    This also presumes that wwlink will be able to infer the confirmation, success and failure messages to use for each action, and whatever else it needs along the way. I think that can be done.

    Side note: Do I want the API action names to be shorter?

    Weird thought: Each of these links needs to also have a non-JS target, which is its href attribute, something like href='//lalashan.mcmaster.ca/theobio/whatever/index.php?title=Special:ManageProject&project=Sandbox&ww-action=create-background-job&action-target=test-mk.out'. All the info is there redundantly. Would it be too horrible to have wwlink() infer the info from that href string? I could impose more regularity on the form of those URL parameters.

    This goes back to the complications I encountered in [#434] ("Keep AJAX DRY"). For one thing, there are clicks, "remove/remake" for example, that don't correspond to a single API action. I guess if I had to I could make them correspond, or use a framework that allows a way to make exceptions.

     

    Related

    Bugs: #434

  • Lee Worden

    Lee Worden - 2014-01-23

    Maybe I can require that all parameters to WWAction have names starting with 'ww-', and no others do? Or rather, no others in URLs that might include 'ww-action'. I think there will be some others in ImportProjectFiles that don't matter.

    That will make it simple to parse out into an API call. I can just write onclick="wwlink()" if it's sufficient to infer the API call from the href, and onclick="wwlink({action:'whatever',whatever:'whatever else'})" if it isn't (or even onclick="wwlink({action='ww-remove-project-file',...});wwlink({action='ww-get-project-file',make=1,...})").

     
  • Lee Worden

    Lee Worden - 2014-01-23

    It's unfortunate that sharing $wwConfirmationsForApiActions between the PHP and JS seems to require passing it to the JS as a config variable, which puts it all in the top of the HTML. This design here seems like it'll also necessitate a $wwSuccessMessagesForApiActions, $wwFailureMessagesForApiActions, and maybe even other things? Most pages won't use these and if they do they won't use much of them, so I'd rather not front-load them all for every page!

    Maybe I can standardize the messages' names and pass them only using the messages infrastructure? That would let me wrap them into ResourceLoader packages that are only loaded when needed. I might be able to only pass a short list of $wwActionsThatNeedConfirmation or something at the top.

    Note before I get too busy with this, I should double-check that config variables can't be late-loaded :).

     
  • Lee Worden

    Lee Worden - 2014-01-25

    I can use mw.message('xyz').exists() to do confirm/alert behavior just by defining certain messages or leaving them undefined. The problem is that they have particular arguments: mw.message('wwb-confirm-destroy-background-job', jobid) vs. mw.message('wwb-confirm-create-background-job', projectname, target). I am frustrated.

    I would really like to have a DRY design, and since the confirmation/success/failure messages for all these actions are used in both PHP and JS, I want to define them in PHP and pass the data to JS without duplicating it. I can pass the messages, e.g.

    'wwb-confirm-create-background-job' => "Are you sure you want to create a background job to make '$1' in project '$2'",
    

    but how to tell it to pass the filename and project name as arguments without writing a PHP function and a JS function?

    Well, that's what $wwConfirmationsForApiActions does. But unless I do something weird like requiring them to have the same parameters, I'll have to provide a similar structure for success and failure messages.

    What if I ignore the standard $1 and $2 structure and put things like $target and $project into the message string???? I think that's a bad idea, though I do like it...

    Or I could write some weird file that generates both JS and PHP code???

     
    • Lee Worden

      Lee Worden - 2014-01-25

      Well, I think I'm just going to have 3 arrays and put them in the page header for now.

       
    • Lee Worden

      Lee Worden - 2014-01-25

      But I can use smart defaults and remove redundant information.

      For action 'ww-X', do a confirmation if message 'ww-X-confirm-message' exists. If so, put a list of arguments to that message in $wwApiMessages (note shortened name).

      Success and failure messages for 'ww-X' are 'ww-X-success' and 'ww-X-failure'. Fall back to default messages if those messages don't exist.

      So $wwApiMessages['ww-X'] may contain a 'args' key, defining args to be used in confirmation/success/failure messages alike, or a 'confirmation-args', etc.

       
      • Lee Worden

        Lee Worden - 2014-01-25

        For the record: message 'ww-X-confirm-button' also needs to exist if confirming (the title for the "yes" button. The "no" button will always be message 'ww-cancel'.).

         
  • Lee Worden

    Lee Worden - 2014-01-30

    Cool, $wwApiMessages is working and is pretty minimal. Leaving it at page top seems okay to me.

    The staged loading seems fine:

    • ext.workingwiki.top is loaded with the page header, contains wwlink() and injectSpinner().
      • wwlink() checks for presence of a 'ww-X-confirm-message' message. If found, loads and calls ext.workingwiki.confirm.js, and if not, loads and calls ext.workingwiki.api.js.
      • this module includes the confirm messages, but not the success and so on.
    • ext.workingwiki.confirm.js is loaded at the end of the page load, unless it's loaded sooner in response to a click. It provides confirmApi(), which pops up a confirmation panel leading to a call into ext.workingwiki.api.js. It loads ext.workingwiki.api.js while the user is looking at the panel.
      • this module contains the messages defining the button names for the confirmation panel.
    • ext.workingwiki.api.js is loaded at the end of the page load, unless it's needed sooner. It provides api(), which calls over to the server and waits for a response. The response is processed by functions in ext.workingwiki.js, which is requested when the response is received - I don't think that part needs to be super-responsive, and the page will often be done by then anyway.
    • ext.workingwiki.js is all the rest of the WW code, loaded at end of page load. It lists all the above modules as dependencies, so they'll all be loaded when it is, if they haven't been already.
      • this is where the actions' success and failure messages go.

    The background-job handling JS is separated out into modules stored in the Background/ directory of the source code. They hook in so that they get loaded with the above modules at the appropriate stages.

    Right now I have the ww-create-background-job and ww-destroy-background-job actions working in the new system, and the automatic ww-list-background-job update. Actually, only some of the create-background-job interfaces.

    So I need to:

    • Fix up the rest of the ww-create-background-job links and buttons
    • Redo the rest of the actions that are in the JS code and get rid of the old click-handling code
    • Get the list background jobs to reload automatically when you do a relevant action. This feature got lost in the redesign.

    Then I'll be done with this redesign, and I can go back to making all the other WW actions have JS links.

     
    • Lee Worden

      Lee Worden - 2014-01-30

      Also:

      • I think that though it's less byte-efficient, I want the actions' url parameters to be called ww-action-X, not just ww-X, because ww-X doesn't really make sense distinguishing some WW terms from other WW terms.
      • I think to get the automatic-reload, I want to look into how the MW JS code does hooks, which I'd like to understand anyway.
       
  • Lee Worden

    Lee Worden - 2014-01-30

    Done, all the above. Ready to deploy to lalashan, I think, when I'm not horizontally bound.

    It's a bit intricate, but also nice and clean. I converted the kill and merge actions from WWAction to Api with ajax links in just a few minutes, because the framework is now there to do pretty much all of it automatically. This is quite promising for the next step, which is to convert all the non-background-job actions, such as sync-source-files, etc.

    The only real exception to the cleanliness is how I did the 'background make' form on the MP page. There's some trick to handling buttons with names in English that get translated, but I haven't used it, whatever it is, I used a nasty hack instead. To convert the regular make button to ajax as well as the background one I'll have to revisit this and do it right.

     
    • Lee Worden

      Lee Worden - 2014-01-30

      BTW the mw.hook() apparatus is really clean and easy to use - I rewrote the reload-list-of-background-jobs code with it and it immediately worked without any debugging whatsoever!

       
  • Lee Worden

    Lee Worden - 2014-01-30

    Oh man, I tried to deploy that and it caused all kinds of trouble.

    Merge conflicts in the live code, causing whitescreens, and version incompatibility in the PHP and JS causing breakage when the code was in fact running.

    I've backed off to an older revision of the WW code. I moved the working copy aside for the time being. Some fixes I made in the live WW code over the last few days are probably reverted, as they're only in the moved-aside code. I'll fix that ASAP.

    These changes to the API/WWAction design need to be worked over in MW 1.19 and 1.21, as there are compatibility issues involving WW code that uses MW features that were added since then.

     
    • Lee Worden

      Lee Worden - 2014-01-30

      Fixes I made in the live code are back in the live code now.

       
  • Lee Worden

    Lee Worden - 2014-01-31

    Meanwhile, back on my laptop:

    Problems in 1.19

    • mw.hook is not there
      • I guess I can just provide a minimal replacement for that
    • Message objects (in js) don't have a text() method
      • use plain() instead
    • Message objects (in php) don't have a getKey() method
      • actually, I don't really need to use that.

    OK, fixed those, now seems pretty good.

    Problems in 1.21 or 1.22, now that I've fixed the above?

    • 1.21 seems ok
    • Now I've made the debouncing a bit more assertive, and turned off the confirmation for ww-create-background-job.
    • 1.22 also seems fine
    • It'd be nice to make the reload-background-jobs control available before page finishes loading, but I can't get excited about that.

    I think it's ready for deploy now, but again I'm going to sleep. Later...

     
  • Lee Worden

    Lee Worden - 2014-01-31

    The page load is kind of quick when the wiki's on my laptop, so I'm looking forward to trying out the while-page's-loading part when it's on lalashan and I'll be able to see if it makes a difference.

     
  • Lee Worden

    Lee Worden - 2014-01-31

    These new JS features are deployed on lalashan now. Seems to be working well on both 1.21 and 1.19.

    Unfortunately :) the response time is good from where I am at UCB, so no word on how well it works when pages are slow...

     
  • Lee Worden

    Lee Worden - 2014-03-20

    Seems like sometimes clicks while the page is loading do the non-JS behavior. What's up with that? And how can I troubleshoot it?

    • Read up on onClick. I've read up some, but saw nothing to suggest that it might not get called.
    • Load and click a bunch of times with the debugger running?
    • Load and click a bunch of times with console logging? That might be good. At least to find out whether it's actually not calling wwlink() or something else is going wrong.

    Followup: made a link for testing on a sandbox page: it calls preventDefault() to stop the browser from going to the link destination, console.log() to record that it happened, and alert() to make it obvious.

    It looks like clicks get lost if they happen while the page is doing the blocking loads in the head element? They don't browse away to the link target though, they just don't do anything at all.

     
    • Lee Worden

      Lee Worden - 2014-03-20

      I notice that the .js file where wwlink() is defined is loaded from load.php, so while it's a blocking load at the beginning of the page, it's not inline in the page source, and I can imagine clicks being possible while waiting for that resource to arrive? I thought it had to be received before any html could be displayed, but it doesn't seem so from the tests I was doing yesterday. I could look into that more. Ideally I'd like to put the minimal wwlink() code right into the page's head element.

       
      • Lee Worden

        Lee Worden - 2014-03-20

        well I tried some different loads from a lalashan wiki and I didn't get it to do the wrong thing.

        Still not finding anything useful in the search engines. Maybe I'll have to wait for a reproducible example of bad behavior. Maybe I'll work on the question of what happens when you middle-click a link, and see if anything emerges.

         

Anonymous
Anonymous

Add attachments
Cancel





Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.