[KSS-checkins] r50918 - kukit/kss.base/trunk/kss/base

jvloothuis at codespeak.net jvloothuis at codespeak.net
Wed Jan 23 15:19:35 CET 2008


Author: jvloothuis
Date: Wed Jan 23 15:19:35 2008
New Revision: 50918

Modified:
   kukit/kss.base/trunk/kss/base/__init__.py
   kukit/kss.base/trunk/kss/base/commands.py
   kukit/kss.base/trunk/kss/base/commands.txt
   kukit/kss.base/trunk/kss/base/corecommands.py
   kukit/kss.base/trunk/kss/base/corecommands.txt
Log:

Made the protocol compatible with the recent changes made to kss.core (which where fixes in problems with the old protocol)


Modified: kukit/kss.base/trunk/kss/base/__init__.py
==============================================================================
--- kukit/kss.base/trunk/kss/base/__init__.py	(original)
+++ kukit/kss.base/trunk/kss/base/__init__.py	Wed Jan 23 15:19:35 2008
@@ -1,3 +1,4 @@
 from kss.base.commands import KSSCommands
 from kss.base.selectors import selectors
 from kss.base.plugin import load_plugins
+from kss.base.commands import xmldata, htmldata, cdatadata

Modified: kukit/kss.base/trunk/kss/base/commands.py
==============================================================================
--- kukit/kss.base/trunk/kss/base/commands.py	(original)
+++ kukit/kss.base/trunk/kss/base/commands.py	Wed Jan 23 15:19:35 2008
@@ -1,21 +1,37 @@
-from xml.sax.saxutils import quoteattr
+from xml.sax.saxutils import quoteattr, escape
 from kss.base.registry import command_set_registry
 from kss.base.selectors import Selector
-from kss.base.selectors import css
 
 kss_response_header = '''<?xml version="1.0" ?>
-<kukit xmlns="http://www.kukit.org/commands/1.0"><commands>
+<kukit xmlns="http://www.kukit.org/commands/1.1"><commands>
 '''
 
 kss_response_footer = '</commands></kukit>'
 
-
 kss_command_start = '<command selector=%(selector)s name=%(action)s selectorType=%(selector_type)s>'
-kss_command_startglobal = '<command name=%(action)s>'
+
+kss_command_start_global = '<command name=%(action)s>'
 
 kss_command_end = '</command>'
 
-kss_param = '<param name=%(name)s><![CDATA[%(value)s]]></param>'
+kss_param = '<param name=%(name)s>%(value)s</param>'
+
+class cdatadata(object):
+    def __init__(self, value):
+        self.value = value
+
+    def node(self):
+        '''Return the XML node representation of this object'''
+        return '<![CDATA[%s]]>' % self.value.replace(']]>', ']]&gt;')
+
+    def __repr__(self):
+        return "%s('%s')" % (self.__class__.__name__, self.value)
+
+class htmldata(cdatadata):
+    pass
+
+class xmldata(cdatadata):
+    pass
 
 class KSSCommands(object):
     '''Command renderer for creating KSS responses'''
@@ -25,9 +41,6 @@
     def add(self, action, selector, **kwargs):
         self._strip_none_parameters(kwargs)
 
-        if selector is not None and not isinstance(selector, Selector):
-            selector = css(selector)
-
         self.commands.append((action, selector, kwargs))
 
     def _strip_none_parameters(self, parameters):
@@ -38,19 +51,37 @@
     def render(self):
         output = [kss_response_header]
         for action, selector, options in self.commands:
-            if selector is not None:
-                # selector command
-                output.append(kss_command_start % dict(
-                    selector=quoteattr(selector.value),
-                    selector_type=quoteattr(selector.type),
-                    action=quoteattr(action)))
+            if selector is None:
+                output.append(kss_command_start_global % dict(
+                        action=quoteattr(action)))
             else:
-                # global command
-                output.append(kss_command_startglobal % dict(
-                    action=quoteattr(action)))
+                try:
+                    selector_type = selector.type
+                    selector = selector.value
+                except AttributeError:
+                    # It is probably a string or unicode object so we
+                    # can let the client decide the default selector
+                    selector_type = ''
+
+                output.append(kss_command_start % dict(
+                        selector=quoteattr(selector),
+                        selector_type=quoteattr(selector_type),
+                        action=quoteattr(action)))
+                
             for name, value in options.items():
+                try:
+                    node = value.node()
+                except AttributeError:
+                    # If it the value does not explicitly convert to a
+                    # node make it a text node, unless it is larger
+                    # than 4KB (since this triggers a problem with
+                    # Firefox and large text nodes).
+                    if len(value)>= 4096:
+                        node = cdatadata(value).node()
+                    else:
+                        node = escape(value)
                 output.append(kss_param % dict(
-                    name=quoteattr(name), value=value))
+                    name=quoteattr(name), value=node))
             output.append(kss_command_end)
         output.append(kss_response_footer)
         return ''.join(output)
@@ -59,18 +90,21 @@
         self.commands = []
 
     def __str__(self):
-        def format_options(options):
-            if not options:
-                return ''
-            return ', ' + ', '.join(
-                ["%s='%s'" % item for item in options.items()])
-
         lines = []
         for action, selector, options in self.commands:
-            lines.append("%(action)s(%(selector)s%(options)s)" % {
-                    'action': action,
-                    'selector': selector,
-                    'options': format_options(options)})
+            line = '%s(' % action
+            if isinstance(selector, Selector):
+                line += '%s' % selector
+            elif isinstance(selector, basestring):
+                line += "'%s'" % selector
+            
+            if options:
+                if selector is not None:
+                    line += ', '
+                line += ', '.join(
+                    ["%s=%r" % (key, value) for 
+                     key, value in options.items()])
+            lines.append(line + ')')
         return '\n'.join(lines)
 
     def __getattr__(self, name):

Modified: kukit/kss.base/trunk/kss/base/commands.txt
==============================================================================
--- kukit/kss.base/trunk/kss/base/commands.txt	(original)
+++ kukit/kss.base/trunk/kss/base/commands.txt	Wed Jan 23 15:19:35 2008
@@ -55,7 +55,32 @@
   [<DOM Element: command at ...>, <DOM Element: command at ...>]
 
 Each parameter is represented by a child node in a command. Their name
-is stored in the attribute and their value is put within CDATA blocks.
+is stored in the attribute and their value is put within text nodes or
+CDATA blocks. Which type of node is used depends on the type of
+the keyword argument given to the command.
+
+Depending on the type the serialisation to XML changes. Strings and
+unicode objects will be send as text nodes. You can also mark data as
+HTML, XML or CDATA blocks. This is used so that these can have special
+escaping rules.
+
+  >>> from kss.base import xmldata, htmldata, cdatadata
+
+Now if we make a command with these types the output will be different.
+
+  >>> commands = KSSCommands()
+  >>> commands.add('htmlAction', css('#someid'), html=htmldata('some value'))
+  >>> commands.add('cdataAction', css('#otherid'), arg=cdatadata('some arg'))
+  >>> commands.add('xmlAction', css('#otherid'), arg=xmldata('some arg'))
+  >>> commands.add('normalAction', css('#otherid'), arg='normal arg')
+  >>> doc = minidom.parseString(commands.render())
+  >>> for command in doc.getElementsByTagName('command'):
+  ...     params = command.getElementsByTagName('param')
+  ...     print params[0].childNodes[0].nodeType == doc.CDATA_SECTION_NODE
+  True
+  True
+  True
+  False
 
   >>> replace_command = doc.getElementsByTagName('command')[0]
 
@@ -70,9 +95,28 @@
   >>> commands.render()
   '...<![CDATA[some value]]>...'
 
-We are using CDATA because Firefox chops text nodes at 4KB blocks
-(which makes the client side handling more difficult). Using CDATA
-avoids the chopping.
+CDATA nodes are used for serializing XML or HTML. This makes the
+response more readable when looking at it from traffic sniffers or
+other debugging tools.
+
+We have another case where CDATA is used. That is when we serialize a
+normal value larger than 4KB. This is because Firefox chops text nodes
+at 4KB blocks (which makes the client side handling more
+difficult). Using CDATA avoids the chopping.
+
+The example below demonstrates this behaviour.
+
+  >>> commands = KSSCommands()
+  >>> commands.add('smallValueAction', '#test', value='tiny')
+  >>> 'CDATA' in commands.render()
+  False
+
+Now run the example again with a larger value.
+
+  >>> commands = KSSCommands()
+  >>> commands.add('smallLargeAction', '#test', value='huge' * 4096)
+  >>> 'CDATA' in commands.render()
+  True
 
 
 String representation
@@ -125,12 +169,12 @@
 
 You can also use strings instead of a selector instance as a value for
 selector. In this case the string will automatically be converted to a
-CSS selector.
+selector on the client. By default this is the CSS selector.
 
   >>> commands.clear()
   >>> commands.add('replaceHTML', '#someid', html='some value')
   >>> print commands
-  replaceHTML(css('#someid'), html='some value')
+  replaceHTML('#someid', html='some value')
 
 
 Command sets
@@ -182,6 +226,6 @@
 
   >>> commands.core.replaceInnerHTML(css('div'), 'example')
   >>> print commands
-  replaceInnerHTML(css('div'), html='example')
+  replaceInnerHTML(css('div'), html=htmldata('example'))
   
-  >>> command_set_registry.unregister('core')
\ No newline at end of file
+  >>> command_set_registry.unregister('core')

Modified: kukit/kss.base/trunk/kss/base/corecommands.py
==============================================================================
--- kukit/kss.base/trunk/kss/base/corecommands.py	(original)
+++ kukit/kss.base/trunk/kss/base/corecommands.py	Wed Jan 23 15:19:35 2008
@@ -1,4 +1,5 @@
 from kss.base.commands import KSSCommandSet
+from kss.base import htmldata
 
 class KSSCoreCommands(KSSCommandSet):
 
@@ -27,7 +28,7 @@
         extra_args = {}
         if not withKssSetup:
             extra_args['withKssSetup'] = 'False'
-        self.commands.add('replaceInnerHTML', selector, html=value, 
+        self.commands.add('replaceInnerHTML', selector, html=htmldata(value), 
                           **extra_args)
 
     def replaceHTML(self, selector, value, withKssSetup=True):

Modified: kukit/kss.base/trunk/kss/base/corecommands.txt
==============================================================================
--- kukit/kss.base/trunk/kss/base/corecommands.txt	(original)
+++ kukit/kss.base/trunk/kss/base/corecommands.txt	Wed Jan 23 15:19:35 2008
@@ -95,15 +95,15 @@
   >>> commands.clear()
   >>> core.replaceInnerHTML(css('div'), 'some html')
   >>> print commands
-  replaceInnerHTML(css('div'), html='some html')
+  replaceInnerHTML(css('div'), html=htmldata('some html'))
 
 You can also avoid KSS event setup. Use this only if you really need
 the speedup because KSS will not be applied to these new nodes.
 
   >>> core.replaceInnerHTML(css('div'), 'some html', withKssSetup=False)
   >>> print commands
-  replaceInnerHTML(css('div'), html='some html')
-  replaceInnerHTML(css('div'), html='some html', withKssSetup='False')
+  replaceInnerHTML(css('div'), html=htmldata('some html'))
+  replaceInnerHTML(css('div'), html=htmldata('some html'), withKssSetup='False')
 
 
 Replace HTML
@@ -261,7 +261,7 @@
   >>> commands.clear()
   >>> core.setStateVar('varname', 'value')
   >>> print commands
-  setStateVar(None, varname='varname', value='value')
+  setStateVar(varname='varname', value='value')
 
 Trigger event
 -------------
@@ -269,4 +269,4 @@
   >>> commands.clear()
   >>> core.triggerEvent('eventname')
   >>> print commands
-  triggerEvent(None, name='eventname')
+  triggerEvent(name='eventname')


More information about the Kukit-checkins mailing list