url preperation for protocol objects.
Implements initialization of an Dispatcher compatible protcol object without commiting to a particular kind of request object. It can also be used as a convenience method for preparing parameters for XMLHttpRequest, but do NOT pass in an instance of XMLHttpRequest as self !
Take care when supplying additional query parameters. If one of your parameter names collides with dispatchparametername, then the first element of the array of values for that parameter is clobbered by the dispatch data.
If you host your backend on the same domain as the client script the parameter sdpp should be left undefined or set to null.
sdpp only comes into play if you need to script accross domains.
sdpp defaults to using the [scheme,domain,port,path] of the currentDocument and the path part is replaced with servicepath. ie., By default, avoid security issues attendant to cross domain scripting.
Couple of handy references for this issue: mozilla signed-scripts.html and apple xmlhttpreq.html
Note, in particular two urls, differing only in the port number, ie:
http://www.mywebhost.com/index.html http://www.mywebhost.com:50000/mybackend.json
Are on different domains as far as the definition of 'cross-domain' is concered. If index.html pulls in java script that references the latter, then that script is crossing domains ...
AFAICT mozilla(Firefox et all), do not support anything like macromedia crossdomain.xml, and arguably shouldn't !
self: victim object
If self is null then the this object is used. If typeof(self) is "undefined" a new object is created. Otherwise self is used as is.
servicepath: path part of the url of the backend
dispatchparametername: url dispatch query parameter name.
Defaults to "dispatch".
names,values: extra query parameters
As per queryString. But note if names contains dispatchparametername it's associated value is replaced with the dispatch data.
sdpp: array of [scheme, domain, port|null, path|null]
Defaults to split_schemedomainandport(currentDocument().URL) path part is always replaced with servicepath
rtype: self (or whatever it became after coercion from null or undefined)
Standard polling protocol.
Implements a Dispatcher compatible dispatch protcol using MochiKit.Async.getXMLHttpRequest as its basis. See prepare_protcol for a detailed explanation of the other parameters.
servicepath: path of the backend resource representing the other end of the circuit.
dispatchparametername,names,values,sdpp: See prepare_protocol(servicepath,dispatchparametername,names,values,sdpp)
Polls the backend using XMLHttpRequest
Initialize a dispatcher.
Send a query to the backend.
Returns a deferred that will be signaled with the result.
body: The body of the query.
d: The deferred to signal when the result is available. If null or undefined one will be created for you.
rtype: Deferred
respond to a query initiated by the backend.
response_id: the id allocated by the backend for its query.
body: the body of your response.
Internal method
Gets chained onto the delegate returned by Dispatcher.poll to consume and dispatch backend responses to client originated queries.
Returns an iterable containing any queries that were intiated by the backend in time for the last poll.
result: The protcol is expected to process the response data into this form:
[iterablequeries, iterableresponses]
=> [
[[queryid1,data1], ... [queryidN,dataN]],
[[responseid1,data1], ... [responseidN,dataN]]
]
For this method, the perspective for interpreting queryid's and responseids is looking from the client towards backend.
Each item in result[0], iterablequeries, is then an asynchronous query from the backend to the client. Similarly, each item in result[1], iterableresponses, is a response from the backend to a query that originated in the client.
This function processes the backend responses and fires the client side delegates that are pending those responses. It returns the backend originated queries for the next callable in the dispatch chain.
rtype: iterablequeries, these are the new queries from the backend to the client. Note: queries with null or "undefined" tags indicate, the backend does not want or expect a response to that query.
Poll the backend exactly once.
start_polling causes this to be called at regular intervals. If you need to force a poll, call stop_polling first.
rtype: Deferred
Set the dispatcher polling.
defaultinterval: The default polling interval If there is no observer or it doesn't specify one, then use this value as the delay between the last result and the next poll.
note: If you need to be clever about retry intervals etc then the observer is the place to do that.
Internal method
call the observer with the backend queries like: observer.handle_queryitems(queryitems, dispatcher)
Note: the call to observer.handle_queryitems precedes this functions check of this.polling and the interval. This is to allow the observer to imediately terminate. the observer is trusted to call stop_polling from within observer.handle_queryitems rather than simply asigning to dispatcher.polling, which would/could break stuff.
It is perfectly resonable for the observer to replace itself with an alternate during observer.handle_queryitems. ie, some handler would do:
dispatcher.stop_polling(); dispatcher.start_polling(otherobserver);
Note: While it is legal for the observer to modify dispatcher.polling_interval in handle_queryitems, setting it to null or "undefined" is not.
Split a url into its constituent parts.
If port and or path parts are missing from the url, the associated fields are set to null:
sdpp => [scheme, domain, port|null, path|null]
Both, join_schemedomainandport and replace_schemedomainandport_path cope with this. Note that:
join(split('http://some.domain')) => 'http://some.domain/'
join(split('http://some.domain:8080')) => 'http://some.domain:8080/'
join(split('http://some.domain/')) => 'http://some.domain/'
join(split('http://some.domain:8080/')) => 'http://some.domain:8080/'
Ie., root path is forced if it is missing.
url: url to split. if null or undefined currentDocument().URL is used.
Compliment of split_schemedomainandport
sdpp: [scheme, domain, port|null, path| null]
Like, join but replacement clobbers path.
This is a shortcut for:
var url = split_schemedomainandport(url); url[3] = replacement; url = join_schemedomainandport(url);
Serialize queued queries.
Into a format appropriate for a GET/POST request parameter. If you need to escape or otherwise encode data elements, see join_json_encodeitems
queryitems: [[queryid1,data1], ... [queryidN,dataN]]
responseitems: [[responseid1,data1], ... [responseidN,dataN]]
rtype: json formated string
join, with custom item encoding.
See join_json
queryitemencoder: called for each datafield in queryitems, should return encode(queryitemdata).
responseitemencoder: called for each datafield in responseitems, should return encode(queryitemdata).
If repsonseitemencoder is null or undefined it defaults to queryitemencoder.
If both responseitemencoder and queryitemencoder are either null or undefined this function short circuits to join_json.
Converse of join_json(queryitems, responseitems).
WARNING: uses eval, You need to decide if you trust data.
data: a json string, assumed to describe an array with the form
[iterablequeries, iterableresponses][[[queryid1,data1], ... [queryidN,dataN]],[[responseid1,data1], ... [responseidN,dataN]]]
rtype: Iter
split_json_decodeitems
don't think it makes sense to provide 'split_json_decodeitems' as it would be highly sensitive to different join_json_encodeitems encoding schemes. If you can't use end to end json, or, more importantly, you decide not to trust eval, then you need to write a custom split for your protocol implementation.
Loopback implementation for Protocol.poll.
Provided for the benefit of test code. Echos query items, encoded in clientdata, as the loopbackresult.
clientdata: the serialized and encoded queries & responses to submit on the clients behalf. response items in clientdata are silently ignored.
latency: how long the poll should take. the default is 0. Note that latency <= 0 is special, it forces synchronous behaviour.
emulatedbackendpollresult: emulated backend responses, Serialized and encoded queries & responses fake response to this poll. By default, response items are not included in the items looped back to the client.
ignoreemulatedresponses: Don't throw away emulated responses. if this parameter is true then emulated backend responses are included in the loopback result.
if latency was zero or unspecified the returned deferred has allready been fired.:
if latency unspecified or latency <= 0
returns succede(loopbackresult)
Otherwise:
wait(latency, loopbackresult)
rtype: Deferred
moribund:
ended up allways passing just the remainder of the tag or null if there wasn't any more.:
if the query specified an inner tag then split it up and pass up:
[innertag,depth,outertag]
protocols should contrive hounour the identity:
fulltag == tag.length > 1 ?
tag[tag[tag.length-1], tag[0]].join(outer.TAG_TERMINATOR) :
tag;
Note: because 'depth' is included in the tag this will *NOT* work:
fulltag == tag.length > 1 ?
tag.reverse().join(outer.TAG_TERMINATOR) :
tag;
if the query did not specify an inner tag then pass up:
[null,depth,outertag]
It is expected that in this case the inner protocol will ignore
the tag completely. However a consistent format allows provision
of default catch all 'global' handlers to be implemented at any
depth.
generaly the top level (inner most depending on your perspective) caller
of the origin query will ignore the tag completely and just deal with 'body'.
On the way up, in each 'inner' protocol, tag[0] will always be the key you
tacked on the the query at that depth, the key tacked on by a more 'inner'
caller, or null if and only if there are no more parts of the tag to consider.