I agree with all those points. I'm reading through the new navigation
code and it's much nicer. I'm very happy to see the clean separation
of tree manipulation and rendering. Thanks for all that hard work Jan!
I have a long specification written up below; I think I've dealt with
the major design issues except the specific encoding restrictions on
parameter names and values and the nitty gritty of cross-browser
fragment updating.
If anyone wants to take the time to read through and ask probing
questions or suggest how to encode hash parameters, I'm all ears.
Thank you,
Ian
=====================
URL Parameter Protocol Spec:
Enabling parameter updating via a mixin class parameter-mixin.
defwidget can add this to the superclass list of widgets when any slot
contains :parameter-p name where name is the parameter string
associated with the slot value. The default methods for non
parameterized widgets simply return nil or do nothing.
The reason for the mixin is that we add overhead to URL processing and
generation when we use automatic parameterization. Users wishing to
customize this can simply override current-parameters and update-
parameters for their specific widget, ignoring the metaclass machinery.
Incoming URLs:
(defgeneric update-widget-parameters (pwidget parameters)
"Given an alist of parameters, widget updates its state
appropriately. The default method for plain widgets does
nothing. The default method for parameter-mixin uses a
method (parameter-slot-map that is automatically defined by
the macro to get the list slots to update and the associated
parameter names.
The default method presumes that parameters take the form of
(format nil "~A-~A" (class-of widget) slotname). This method is
called
after the update-children call during widget updating."
Conflicts and multiple-instances of a widget:
This scheme falls down when you have multiple versions of a widget
class using parameters on the same page. We leave resolution of these
problems to the programmer, suggesting that they bypass the automated
mechanism.
Generating the Current URL:
If you want to generate a link that encodes the link's side effect
into the url parameter arguments, then we need a way to easily
generate the current URL for the other widgets in the post-action,
post-parameter processing state. However, the contract is that the
state of the hierarchy given a URI should mirror the semantics of that
URI. So given the current URI and parameters, all we need to do is
merge the desired side effects into the current URI. Let's just give
the user a default helper function:
(defgeneric merge-with-current-uri (webapp new-params)
"This is a webapp scope method that implements a simple policy for
taking the union of all provided parameters, giving preference
to changes.")
==================================
URL Fragment Protocol Spec:
To review, URL fragments are used for:
1) Supporting back button state changes without page loads
2) Recording client-side state changes w/o server side updates
3) Bookmarking ajax state
All of this amounts to synchronizing significant state (for user-
driven recreation via backbutton or bookmarks) between the URL bar,
the client state and the server state. When any one area changes, the
others are to be updated. This is done via polling the URL variable
for changes (there is no event, last I checked, that you can listen
to). We do not enable polling unless the server or client side sends
a non-empty fragment or the client side registers a callback.
We need a server-side as well as a client-side protocol. On the
server side we simply need to serialize a subset of tree state into a
fragment and be able to reproduce that state when it is passed back
from the client. Client-side we need to handle the back button, and
on a fresh page load detect whether the server hash matches ours and
if not, ask the server to sync up by processing the hash fragment.
We add a new field to the ajax protocol that contains the current URL
fragment (simpler to debug than a script). However, we may not get
all the state on an ajax call because all dirty widgets are not
touched so what we're getting is a diff which needs to be merged via a
simple policy.
To keep this simple, I don't intended it to integrate it into the
parameter-mixin protocol (for now). Theoretically, we could merge
these two mechanisms so we have a fallback for non-javascript
browsers; but I feel that is overly restrictive and few of us are
programming for a non-javascript audience. Don't use identifiers if
you want a fallback mode.
It is assumed that users will define their own space of parameters and
ensure that they are disjoint.
(defgeneric render-widget-fragment (widget)
"Simply returns an alist of key, value pairs representing the
current dynamic state of that widget. It is merged into the
current request's parameter list and shipped to the client.
This function is called during rendering and accumulates
key-value pairs in a global variable, doing duplicate detection."
During normal requests, the result list is combined into a fragment
and sent via a script. During ajax requests, a subset of parameters
are sent and the client merges them with the current fragment to
update the history.
(defgeneric update-widget-fragment (widget alist)
"The widget updates its state using the fragment and marks itself
dirty iff anything has changed. This is only called via ajax."
There is a new test in the request-handler for a special URL parameter
'fragment' which, like 'action', is reserved and used for AJAX
updates. When this parameter is set, it contains the URL fragment and
walk-widget-tree is used to update the current set of active widgets.
Client-side:
The client has to handle only two events:
- backbutton changes the fragment (send full fragment to server)
- ajax call resulted in a fragment change (update URI fragment, update
history)
This is all hidden inside the weblocks.js code. However, we export
one function and support a callback for client-only fragment state
(e.g. a visualization zoom level where we want to avoid server calls,
but record state change). This can be done via an :before-load script
or from a widget js file.
function change-client-fragment (param-dict)
Called to store parameters in the URL fragment. We will separate
the client and server parameter namespaces to simplify things.
function register-client-fragment-handler (fn)
Called after load by the success handler for the server-side state
update. The client-side updates always happen in the configuration
determined by the server URL path, URL parameters, and fragment
parameters. The function simply accepts a dictionary with the updated
parameters.
On Mar 21, 2009, at 6:20 PM, Jan Rychter wrote:
>
> Ian Eslick <[email protected]> writes:
>> I think that's the idea; let's not overcommit until we see how people
>> use it. Any other comments or feedback? I think some basic support
>> could be provided fairly quickly with some careful surgery on the
>> walk-
>> widget method so we can experiment. A sprint if you will...
>
> Sounds good so far -- IMHO update-widget-tree (right after the
> update-children call) is the place to do it, but someone needs to
> design
> an interface/protocol. By that I mean something similar to the
> update-children/get-widget-for-tokens/update-dependents trio used for
> handling URI tokens and selection within the tree.
>
> There is also the question of whether parameters can modify the tree
> structure. I would suggest avoiding this.
>
> --J.
>
>> On Mar 21, 2009, at 1:50 PM, Jan Rychter wrote:
>>
>>>
>>> Ian Eslick <[email protected]> writes:
>>> [...]
>>>> The first is pretty easy, I hope. As we walk the widget tree
>>>> invoking
>>>> navigation functionality, we have a new method that is called on
>>>> each
>>>> widget that can be specialized to extract URL parameters and update
>>>> widget state without messing with rendering - essentially a
>>>> potential
>>>> action tied to each widget. Perhaps this is already effectively
>>>> there? Individual users can decide how to create hard links that
>>>> generate these parameters.
>>>
>>> Do we leave the naming of the parameters up to application
>>> developers?
>>> E.g. it is up to each widget to decide what it wants to extract?
>>>
>>> I suppose so, because if we try to force any structure we basically
>>> end
>>> up with a duplicate of the uri-token selector scheme, only for URL
>>> parameters...
>>>
>>> --J.
>>>
>>>>
>>
>>
>>
>
> >
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"weblocks" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/weblocks?hl=en
-~----------~----~----~----~------~----~------~--~---