File python-toscawidgets-js-30.patch of Package python-ToscaWidgets
diff -r 45c0f713ff65 tw/core/js.py
--- a/tw/core/js.py Tue Aug 25 10:54:48 2009 +0200
+++ b/tw/core/js.py Thu Aug 27 09:52:38 2009 -0400
@@ -1,26 +1,27 @@
"""
Python-JS interface to dynamically create JS function calls from your widgets.
-This moudle doesn't aim to serve as a Python-JS "translator". You should code
+This module doesn't aim to serve as a Python-JS "translator". You should code
your client-side code in JavaScript and make it available in static files which
you include as JSLinks or inline using JSSources. This module is only intended
-as a "bridge" or interface between Python and JavaScript so JS function
+as a "bridge" or interface between Python and JavaScript so JS function
**calls** can be generated programatically.
"""
-import sys
+import sys
import logging
from itertools import imap
+import re
import simplejson.encoder
-__all__ = ["js_callback", "js_function", "js_symbol", "encode"]
+__all__ = ["js_callback", "js_function", "js_symbol"]
log = logging.getLogger(__name__)
class TWEncoder(simplejson.encoder.JSONEncoder):
"""A JSON encoder that can encode Widgets, js_calls, js_symbols and
js_callbacks.
-
+
Example::
>>> encode = TWEncoder().encode
@@ -31,40 +32,56 @@
>>> w = Widget("foo")
>>> args = {'onLoad': js_callback(js_function('jQuery')(w).click(js_symbol('onClick')))}
>>> print encode(args)
- {"onLoad": function(){jQuery(\\"foo\\").click(onClick)}}
+ {"onLoad": function(){jQuery("foo").click(onClick)}}
>>> print encode({'args':args})
- {"args": {"onLoad": function(){jQuery(\\"foo\\").click(onClick)}}}
+ {"args": {"onLoad": function(){jQuery("foo").click(onClick)}}}
-
-
+ Technical note: simplejson does not support arbitrary js injection, which
+ is needed in this module. We work around this by inserting distinct
+ tokens into the JSON encoding stream that can later be replaced with the
+ objects' js representations.
+
"""
def __init__(self, *args, **kw):
- self.pass_through = (_js_call, js_callback, js_symbol, js_function)
super(TWEncoder, self).__init__(*args, **kw)
def default(self, obj):
- if isinstance(obj, self.pass_through):
+ if hasattr(obj, 'get_js_repr'):
return self.mark_for_escape(obj)
elif hasattr(obj, '_id'):
return str(obj.id)
return super(TWEncoder, self).default(obj)
def encode(self, obj):
+ self.unescape_symbols = {}
encoded = super(TWEncoder, self).encode(obj)
- return self.unescape_marked(encoded)
+ unescaped = self.unescape_marked(encoded)
+ self.unescape_symbols = {}
+ return unescaped
def mark_for_escape(self, obj):
- return '*#*%s*#*' % obj
+ self.unescape_symbols[id(obj)] = obj
+ return 'TWEncoder_unescape_' + str(id(obj))
def unescape_marked(self, encoded):
- return encoded.replace('"*#*','').replace('*#*"', '')
-
+ unescape_pattern = re.compile('"TWEncoder_unescape_([0-9]*)"')
+ def unescape(match):
+ try:
+ obj_id = int(match.group(1))
+ obj = self.unescape_symbols[obj_id]
+ return obj.get_js_repr()
+ except:
+ # Simply return the match if there is a problem
+ return match.group(0)
+ return unescape_pattern.sub(unescape, encoded)
class js_symbol(object):
def __init__(self, name):
self._name = name
+ def get_js_repr(self):
+ return str(self._name)
def __str__(self):
- return str(self._name)
+ return self.get_js_repr()
class js_callback(object):
"""A js function that can be passed as a callback to be called
@@ -91,13 +108,13 @@
'function(){foo(1, 2, 3)}'
# A more realistic example
-
+
>>> jQuery = js_function('jQuery')
>>> my_cb = js_callback('function() { alert(this.text)}')
>>> on_doc_load = jQuery('#foo').bind('click', my_cb)
>>> call = jQuery(js_callback(on_doc_load))
>>> print call
- jQuery(function(){jQuery(\\"#foo\\").bind(\\"click\\", function() { alert(this.text)})})
+ jQuery(function(){jQuery("#foo").bind("click", function() { alert(this.text)})})
"""
def __init__(self, cb, *args):
@@ -112,9 +129,12 @@
def __call__(self, *args):
raise TypeError("A js_callback cannot be called from Python")
+
+ def get_js_repr(self):
+ return self.cb
def __str__(self):
- return self.cb
+ return self.get_js_repr()
class js_function(object):
"""A JS function that can be "called" from python and and added to
@@ -124,7 +144,7 @@
Used to create a callable object that can be called from your widgets to
trigger actions in the browser. It's used primarily to initialize JS code
programatically. Calls can be chained and parameters are automatically
- json-encoded into something JavaScript understands. Example::
+ json-encoded into something JavaScript undersrtands. Example::
.. sourcecode:: python
@@ -137,7 +157,7 @@
If made at Widget initialization those calls will be placed in
the template for every request that renders the widget.
-
+
.. sourcecode:: python
>>> from tw.api import Widget
@@ -151,7 +171,7 @@
... )
If we want to dynamically make calls on every request, we ca also add_calls
- inside the ``update_params`` method.
+ inside the ``update_params`` method.
.. sourcecode:: python
@@ -163,12 +183,12 @@
... self.add_call(
... jQuery('#%s' % d.id).datePicker(d.pickerOptions)
... )
-
+
This would allow to pass different options to the datePicker on every
display.
- JS calls are rendered by the same mechanisms that render required css and
- js for a widget and places those calls at bodybottom so DOM elements which
+ JS calls are rendered by the same mechanisms that render required css and
+ js for a widget and places those calls at bodybottom so DOM elements which
we might target are available.
Examples:
@@ -188,42 +208,50 @@
"""
def __init__(self, name):
self.__name = name
-
+
def __call__(self, *args):
return _js_call(self.__name, [], args, called=True)
-
+
+ def get_js_repr(self):
+ return str(self.__name)
+
+ def __str__(self):
+ return self.get_js_repr()
+
class _js_call(object):
__slots__ = ('__name', '__call_list', '__args', '__called')
-
+
def __init__(self, name, call_list, args=None, called=False):
self.__name = name
self.__args = args
- call_list.append(self)
- self.__call_list = call_list
+ self.__call_list = call_list[:]
+ self.__call_list.append(self)
self.__called = called
def __getattr__(self, name):
return self.__class__(name, self.__call_list)
-
+
def __call__(self, *args):
self.__args = args
self.__called = True
return self
-
- def __get_js_repr(self):
+
+ def get_js_repr(self):
+ encoder = TWEncoder()
+ return '.'.join(c.__get_js_repr_fragment(encoder) for c in self.__call_list)
+
+ def __get_js_repr_fragment(self, encoder):
if self.__called:
args = self.__args
- return '%s(%s)' % (self.__name, ', '.join(imap(encode, args)))
+ return '%s(%s)' % (self.__name, ', '.join(imap(encoder.encode, args)))
else:
return self.__name
-
+
def __str__(self):
if not self.__called:
raise TypeError('Last element in the chain has to be called')
- return '.'.join(c.__get_js_repr() for c in self.__call_list)
-
+ return self.get_js_repr()
+
def __unicode__(self):
return str(self).decode(sys.getdefaultencoding())
-
-encode = TWEncoder().encode