%flags>
# myghtyjax.myt needs to inherit from either None, or an autohandler
# that doesnt have any textual output.
inherit=None
trim="both"
%flags>
<%doc>
MyghtyJax - a Myghty AJAX Adapter
The "init" method is called to register the names of javascript functions that will
map to Myghty components and/or python function definitions. supply
key=value arguments, where keys are the names of javascript functions
to be generated and the value is one of: a function object, the string
address of a Myghty component (path or method name), or a Myghty component object.
This method should be called in the %init section of the calling component, usually before
anything else has executed. If it returns true it means the XMLHttpRequest action
is taking effect, and the calling component should return immediately.
If false is returned, the component should continue on normally.
Note that the "js" method below must be called within the HTML body of a page to
actually write out the javascript functions defined by the init method.
components that are called can specify options for how they should be handled via their
<%attr> sections. the available options are:
type='source'
take the HTML source of the component and send it to a supplied javascript
callback function.
type='writedom'
take the HTML source of the component and send it to the DOM element
with the id given by either the <%attr> 'dom_id', or the first argument
to the javascript function.
type='exec'
take the output of the component and evaluate it directly as javascript.
Example:
if m.comp('myghtyjax.myt:init',
mypage = 'SELF:mymethod'
): return
The above example will create a javascript function 'mypage()', which results in an Ajax
call back to the myghtyjax.myt page which will route the request to the <%method mymethod>
inside the current page. The results of the method will be processed per the "type"
attribute on the method.
A second form of init argument allows the values normally inside <%attr> to be
specified directly to init:
if m.comp('myghtyjax.myt:init',
mypage = {
'component' : 'SELF:mymethod',
'exectype' : 'writedom',
'dom_id' : 'leftnav'
}
): return
The above example will create a javascript function 'mypage()', which results in an Ajax
call back to the myghtyjax.myt page which will route the request to the <%method mymethod>
inside the current page. The resulting output from the method will be written to the
'leftnav' DOM element on the page.
There is also a way to circumvent the usual URL of the myghtyjax controller
directly to any URL:
if m.comp('myghtyjax.myt:init',
mypage = {
'handler_uri' : '/my_ajax_page/',
'exectype' : 'writedom',
'dom_id' : 'leftnav'
}
): return
The above example will create a javascript function 'mypage()', which results in an Ajax
call directly to the uri '/my_ajax_page/', and the resulting HTML will be written into the
DOM element 'leftnav'.
%doc>
<%method init>
<%init>
frame = m.execution_stack.pop()
try:
ret = init(m, **ARGS)
finally:
m.execution_stack.append(frame)
return ret
%init>
%method>
<%doc>
js is called after init has been called, and delivers the javascript "stub" functions
that connect page javascript to server side calls via XMLHttpRequest.
the scripturi and handleruri arguments are optional arguments that
reference the web-accessible URIs of the myghtyjax.js and myghtyjax.myt
files respectively.
If these parameters are not supplied, they will be searched for in the interpreter
attributes "myghtyjax.handleruri" and "myghtyjax.scripturi".
if not there either, they will be determined based on the path of the original
calling component.
%doc>
<%method js>
<%args>
scripturi = None
handleruri = None
%args>
<%init>
js(m, handleruri = handleruri, scripturi = scripturi)
%init>
%method>
<%args>
_mjax_def
_mjax_component
%args>
<%init>
m.comp(_mjax_component, **ARGS)
%init>
<%once>
import inspect, string, types, posixpath
import myghty.request as request
# initialization method, receives javascript function names mapped
# to python function pointers, component names, or component objects.
# referenced by the "init" myghty method.
def init(m, **params):
run_func = m.request_args.get('_mjax_def', None)
if (run_func is not None):
object = params[run_func]
callable = _get_callable(m, run_func, object)
callable.run(m)
return True
else:
defs = m.attributes.setdefault('__mjax_defs', {})
for (jsname, object) in params.iteritems():
defs[jsname] = _get_callable(m, jsname, object)
return False
def _get_callable(m, name, object):
if isinstance(object, types.FunctionType):
return DefCallable(name, object)
elif isinstance(object, dict):
return ComponentCallable(m, name, **object)
else:
return ComponentCallable(m, name, object)
def js(m, handleruri = None, scripturi = None):
try:
defs = m.attributes['__mjax_defs']
except KeyError:
raise "myghtyjax.js() method requires components and defs to be init()ialized first"
path = m.current_component.path
if handleruri is None:
handleruri = m.interpreter.attributes.get('myghtyjax.handleruri', path)
if scripturi is None:
scripturi = m.interpreter.attributes.get('myghtyjax.scripturi', None)
if scripturi is None:
(dir, file) = posixpath.split(path)
scripturi = posixpath.join(dir, "myghtyjax.js")
m.write("\n" % scripturi)
m.write("\n")
class JaxComponent:
def init_callerpath(self, m):
if getattr(self, 'callerpath',None) is None:
self.callerpath = m.caller.path
def get_function_args(self, leading_comma = False):
args = string.join(self.argnames + ['_mjax_named'], ',')
if leading_comma and args:
return ', ' + args
else:
return args
def get_handler_uri(self, handler_uri):
return handler_uri
def get_remote_args(self, m):
return string.join(
[
"'_mjax_def' : '%s'" % self.jsname,
"'_mjax_component' : '%s'" % self.callerpath,
"'_mjax_named':_mjax_named"
] +
["'%s' : %s" % (name, name) for name in self.argnames],
",\n",
)
class ComponentCallable(JaxComponent):
def __init__(self, m, jsname, component=None, exectype=None, argnames=None, dom_id=None, handler_uri=None):
self.jsname = jsname
self.component = component
self.handler_uri=handler_uri
if type(component) == types.StringType:
componentobj = m.fetch_component(component)
else:
componentobj = component
if exectype is None:
if componentobj is not None:
self.exectype = componentobj.attributes.get('type', 'exec')
else:
self.exectype = 'exec'
else:
self.exectype = exectype
if dom_id is None and componentobj is not None:
self.dom_id = componentobj.attributes.get('dom_id', None)
else:
self.dom_id = dom_id
if argnames is None:
if componentobj is not None:
self.argnames = [arg.name for arg in componentobj.arguments]
else:
self.argnames = []
else:
self.argnames = argnames
def get_handler_uri(self, handler_uri):
if self.handler_uri is not None:
return self.handler_uri
else:
return handler_uri
def run(self, m):
m.comp(self.component, **m.root_request_args)
class DefCallable(JaxComponent):
def __init__(self, jsname, defobj):
self.jsname = jsname
if isinstance(defobj, types.MethodType):
defobj = defobj.im_func
self.defobj = defobj
self.argnames = inspect.getargspec(defobj)[0]
self.has_named = inspect.getargspec(defobj)[2] is not None
self.exectype = 'exec'
def run(self, m):
if self.has_named:
m.write(self.defobj(**m.root_request_args))
else:
args = {}
for name in self.argnames:
args[name] = m.root_request_args[name]
m.write(self.defobj(**args))
%once>
<%method write_docall>
<%args>
callable
handleruri
%args>
function <% callable.jsname %>(callback<% callable.get_function_args(leading_comma = True) %>) {
doCall('<% callable.get_handler_uri(handleruri) %>', callback, {
<% callable.get_remote_args(m) %>
});
}
%method>
<%method write_remotejs>
<%args>
callable
handleruri
%args>
function <% callable.jsname %>(<% callable.get_function_args() %>) {
runRemoteJS('<% callable.get_handler_uri(handleruri) %>', {
<% callable.get_remote_args(m) %>
});
}
%method>
<%method write_writedom>
<%args>
callable
handleruri
%args>
% if callable.dom_id is None:
function <% callable.jsname %>(domid <% callable.get_function_args(leading_comma = True) %>) {
populateDOM(domid, '<% callable.get_handler_uri(handleruri) %>', {
<% callable.get_remote_args(m) %>
});
}
% else:
function <% callable.jsname %>(<% callable.get_function_args() %>) {
populateDOM('<% callable.dom_id %>', '<% callable.get_handler_uri(handleruri) %>', {
<% callable.get_remote_args(m) %>
});
}
%
%method>