Index: third_party/twisted_8_1/twisted/web/woven/widgets.py |
diff --git a/third_party/twisted_8_1/twisted/web/woven/widgets.py b/third_party/twisted_8_1/twisted/web/woven/widgets.py |
deleted file mode 100644 |
index 6028338cd88962cd57099d16e45cc1f20b9b4420..0000000000000000000000000000000000000000 |
--- a/third_party/twisted_8_1/twisted/web/woven/widgets.py |
+++ /dev/null |
@@ -1,1036 +0,0 @@ |
-# -*- test-case-name: twisted.web.test.test_woven -*- |
-# Copyright (c) 2001-2004 Twisted Matrix Laboratories. |
-# See LICENSE for details. |
- |
- |
-# DOMWidgets |
- |
-from __future__ import nested_scopes |
- |
-import urllib |
-import warnings |
-from twisted.web.microdom import parseString, Element, Node |
-from twisted.web import domhelpers |
- |
- |
-#sibling imports |
-import model |
-import template |
-import view |
-import utils |
-import interfaces |
- |
-from twisted.python import components, failure |
-from twisted.python import reflect |
-from twisted.python import log |
-from twisted.internet import defer |
- |
-viewFactory = view.viewFactory |
-document = parseString("<xml />", caseInsensitive=0, preserveCase=0) |
- |
-missingPattern = Element("div", caseInsensitive=0, preserveCase=0) |
-missingPattern.setAttribute("style", "border: dashed red 1px; margin: 4px") |
- |
-""" |
-DOMWidgets are views which can be composed into bigger views. |
-""" |
- |
-DEBUG = 0 |
- |
-_RAISE = 1 |
- |
-class Dummy: |
- pass |
- |
-class Widget(view.View): |
- """ |
- A Widget wraps an object, its model, for display. The model can be a |
- simple Python object (string, list, etc.) or it can be an instance |
- of L{model.Model}. (The former case is for interface purposes, so that |
- the rest of the code does not have to treat simple objects differently |
- from Model instances.) |
- |
- If the model is-a Model, there are two possibilities: |
- |
- - we are being called to enable an operation on the model |
- - we are really being called to enable an operation on an attribute |
- of the model, which we will call the submodel |
- |
- @cvar tagName: The tag name of the element that this widget creates. If this |
- is None, then the original Node will be cloned. |
- @cvar wantsAllNotifications: Indicate that this widget wants to recieve every |
- change notification from the main model, not just notifications that affect |
- its model. |
- @ivar model: If the current model is an L{model.Model}, then the result of |
- model.getData(). Otherwise the original object itself. |
- """ |
- # wvupdate_xxx method signature: request, widget, data; returns None |
- |
- # Don't do lots of work setting up my stacks; they will be passed to me |
- setupStacks = 0 |
- |
- # Should we clear the node before we render the widget? |
- clearNode = 0 |
- |
- # If some code has to ask if a widget is livePage, the answer is yes |
- livePage = 1 |
- |
- tagName = None |
- def __init__(self, model = None, submodel = None, setup = None, controller = None, viewStack=None, *args, **kwargs): |
- """ |
- @type model: L{interfaces.IModel} |
- |
- @param submodel: see L{Widget.setSubmodel} |
- @type submodel: String |
- |
- @type setup: Callable |
- """ |
- self.errorFactory = Error |
- self.controller = controller |
- self.become = None |
- self._reset() |
- view.View.__init__(self, model) |
- self.node = None |
- self.templateNode = None |
- if submodel: |
- self.submodel = submodel |
- else: |
- self.submodel = "" |
- if setup: |
- self.setupMethods = [setup] |
- else: |
- self.setupMethods = [] |
- self.viewStack = viewStack |
- self.initialize(*args, **kwargs) |
- |
- def _reset(self): |
- self.attributes = {} |
- self.slots = {} |
- self._children = [] |
- |
- def initialize(self, *args, **kwargs): |
- """ |
- Use this method instead of __init__ to initialize your Widget, so you |
- don't have to deal with calling the __init__ of the superclass. |
- """ |
- pass |
- |
- def setSubmodel(self, submodel): |
- """ |
- I use the submodel to know which attribute in self.model I am responsible for |
- """ |
- self.submodel = submodel |
- |
- def getData(self, request=None): |
- """ |
- I have a model; however since I am a widget I am only responsible |
- for a portion of that model. This method returns the portion I am |
- responsible for. |
- |
- The return value of this may be a Deferred; if it is, then |
- L{setData} will be called once the result is available. |
- """ |
- return self.model.getData(request) |
- |
- def setData(self, request=None, data=None): |
- """ |
- If the return value of L{getData} is a Deferred, I am called |
- when the result of the Deferred is available. |
- """ |
- self.model.setData(request, data) |
- |
- def add(self, item): |
- """ |
- Add `item' to the children of the resultant DOM Node of this widget. |
- |
- @type item: A DOM node or L{Widget}. |
- """ |
- self._children.append(item) |
- |
- def appendChild(self, item): |
- """ |
- Add `item' to the children of the resultant DOM Node of this widget. |
- |
- @type item: A DOM node or L{Widget}. |
- """ |
- self._children.append(item) |
- |
- def insert(self, index, item): |
- """ |
- Insert `item' at `index' in the children list of the resultant DOM Node |
- of this widget. |
- |
- @type item: A DOM node or L{Widget}. |
- """ |
- self._children.insert(index, item) |
- |
- def setNode(self, node): |
- """ |
- Set a node for this widget to use instead of creating one programatically. |
- Useful for looking up a node in a template and using that. |
- """ |
- # self.templateNode should always be the original, unmutated |
- # node that was in the HTML template. |
- if self.templateNode == None: |
- self.templateNode = node |
- self.node = node |
- |
- def cleanNode(self, node): |
- """ |
- Do your part, prevent infinite recursion! |
- """ |
- if not DEBUG: |
- if node.attributes.has_key('model'): |
- del node.attributes['model'] |
- if node.attributes.has_key('view'): |
- del node.attributes['view'] |
- if node.attributes.has_key('controller'): |
- del node.attributes['controller'] |
- return node |
- |
- def generate(self, request, node): |
- data = self.getData(request) |
- if isinstance(data, defer.Deferred): |
- data.addCallback(self.setDataCallback, request, node) |
- data.addErrback(utils.renderFailure, request) |
- return data |
- return self._regenerate(request, node, data) |
- |
- def _regenerate(self, request, node, data): |
- self._reset() |
- self.setUp(request, node, data) |
- for setupMethod in self.setupMethods: |
- setupMethod(request, self, data) |
- # generateDOM should always get a reference to the |
- # templateNode from the original HTML |
- result = self.generateDOM(request, self.templateNode or node) |
- if DEBUG: |
- result.attributes['woven_class'] = reflect.qual(self.__class__) |
- return result |
- |
- def setDataCallback(self, result, request, node): |
- if isinstance(self.getData(request), defer.Deferred): |
- self.setData(request, result) |
- data = self.getData(request) |
- if isinstance(data, defer.Deferred): |
- import warnings |
- warnings.warn("%r has returned a Deferred multiple times for the " |
- "same request; this is a potential infinite loop." |
- % self.getData) |
- data.addCallback(self.setDataCallback, request, node) |
- data.addErrback(utils.renderFailure, request) |
- return data |
- |
- newNode = self._regenerate(request, node, result) |
- returnNode = self.dispatchResult(request, node, newNode) |
- # isinstance(Element) added because I was having problems with |
- # this code trying to call setAttribute on my RawTexts -radix 2003-5-28 |
- if hasattr(self, 'outgoingId') and isinstance(returnNode, Element): |
- returnNode.attributes['id'] = self.outgoingId |
- self.handleNewNode(request, returnNode) |
- self.handleOutstanding(request) |
- if self.subviews: |
- self.getTopModel().subviews.update(self.subviews) |
- self.controller.domChanged(request, self, returnNode) |
- |
- ## We need to return the result along the callback chain |
- ## so that any other views which added a setDataCallback |
- ## to the same deferred will get the correct data. |
- return result |
- |
- def setUp(self, request, node, data): |
- """ |
- Override this method to set up your Widget prior to generateDOM. This |
- is a good place to call methods like L{add}, L{insert}, L{__setitem__} |
- and L{__getitem__}. |
- |
- Overriding this method obsoletes overriding generateDOM directly, in |
- most cases. |
- |
- @type request: L{twisted.web.server.Request}. |
- @param node: The DOM node which this Widget is operating on. |
- @param data: The Model data this Widget is meant to operate upon. |
- """ |
- pass |
- |
- def generateDOM(self, request, node): |
- """ |
- @returns: A DOM Node to replace the Node in the template that this |
- Widget handles. This Node is created based on L{tagName}, |
- L{children}, and L{attributes} (You should populate these |
- in L{setUp}, probably). |
- """ |
- if self.become: |
- #print "becoming" |
- become = self.become |
- self.become = None |
- parent = node.parentNode |
- node.parentNode = None |
- old = node.cloneNode(1) |
- node.parentNode = parent |
- gen = become.generateDOM(request, node) |
- if old.attributes.has_key('model'): |
- del old.attributes['model'] |
- del old.attributes['controller'] |
- gen.appendChild(old) |
- self.node = gen |
- return gen |
- if DEBUG: |
- template = node.toxml() |
- log.msg(template) |
- if not self.tagName: |
- self.tagName = self.templateNode.tagName |
- if node is not self.templateNode or self.tagName != self.templateNode.tagName: |
- parent = node.parentNode |
- node = document.createElement(self.tagName, caseInsensitive=0, preserveCase=0) |
- node.parentNode = parent |
- else: |
- parentNode = node.parentNode |
- node.parentNode = None |
- if self.clearNode: |
- new = node.cloneNode(0) |
- else: |
- new = node.cloneNode(1) |
- node.parentNode = parentNode |
- node = self.cleanNode(new) |
- #print "NICE CLEAN NODE", node.toxml(), self._children |
- node.attributes.update(self.attributes) |
- for item in self._children: |
- if hasattr(item, 'generate'): |
- parentNode = node.parentNode |
- node.parentNode = None |
- item = item.generate(request, node.cloneNode(1)) |
- node.parentNode = parentNode |
- node.appendChild(item) |
- #print "WE GOT A NODE", node.toxml() |
- self.node = node |
- return self.node |
- |
- def modelChanged(self, payload): |
- request = payload.get('request', None) |
- if request is None: |
- request = Dummy() |
- request.d = document |
- oldNode = self.node |
- if payload.has_key(self.submodel): |
- data = payload[self.submodel] |
- else: |
- data = self.getData(request) |
- newNode = self._regenerate(request, oldNode, data) |
- returnNode = self.dispatchResult(request, oldNode, newNode) |
- # shot in the dark: this seems to make *my* code work. probably will |
- # break if returnNode returns a Deferred, as it's supposed to be able |
- # to do -glyph |
-# self.viewStack.push(self) |
-# self.controller.controllerStack.push(self.controller) |
- self.handleNewNode(request, returnNode) |
- self.handleOutstanding(request) |
- self.controller.domChanged(request, self, returnNode) |
- |
- def __setitem__(self, item, value): |
- """ |
- Convenience syntax for adding attributes to the resultant DOM Node of |
- this widget. |
- """ |
- assert value is not None |
- self.attributes[item] = value |
- |
- setAttribute = __setitem__ |
- |
- def __getitem__(self, item): |
- """ |
- Convenience syntax for getting an attribute from the resultant DOM Node |
- of this widget. |
- """ |
- return self.attributes[item] |
- |
- getAttribute = __getitem__ |
- |
- def setError(self, request, message): |
- """ |
- Convenience method for allowing a Controller to report an error to the |
- user. When this is called, a Widget of class self.errorFactory is instanciated |
- and set to self.become. When generate is subsequently called, self.become |
- will be responsible for mutating the DOM instead of this widget. |
- """ |
- #print "setError called", self |
- id = self.attributes.get('id', '') |
- |
- self.become = self.errorFactory(self.model, message) |
- self.become['id'] = id |
-# self.modelChanged({'request': request}) |
- |
- def getTopModel(self): |
- """Get a reference to this page's top model object. |
- """ |
- top = self.model |
- while top.parent is not None: |
- top = top.parent |
- return top |
- |
- def getAllPatterns(self, name, default=missingPattern, clone=1, deep=1): |
- """Get all nodes below this one which have a matching pattern attribute. |
- """ |
- if self.slots.has_key(name): |
- slots = self.slots[name] |
- else: |
- sm = self.submodel.split('/')[-1] |
- slots = domhelpers.locateNodes(self.templateNode, name + 'Of', sm) |
- if not slots: |
-# slots = domhelpers.locateNodes(self.templateNode, "pattern", name, noNesting=1) |
- matcher = lambda n, name=name: isinstance(n, Element) and \ |
- n.attributes.has_key("pattern") and n.attributes["pattern"] == name |
- recurseMatcher = lambda n: isinstance(n, Element) and not n.attributes.has_key("view") and not n.attributes.has_key('model') |
- slots = domhelpers.findNodesShallowOnMatch(self.templateNode, matcher, recurseMatcher) |
- if not slots: |
- msg = 'WARNING: No template nodes were found '\ |
- '(tagged %s="%s"'\ |
- ' or pattern="%s") for node %s (full submodel path %s)' % (name + "Of", |
- sm, name, self.templateNode, `self.submodel`) |
- if default is _RAISE: |
- raise Exception(msg) |
- if DEBUG: |
- warnings.warn(msg) |
- if default is missingPattern: |
- newNode = missingPattern.cloneNode(1) |
- newNode.appendChild(document.createTextNode(msg)) |
- return [newNode] |
- if default is None: |
- return None |
- return [default] |
- self.slots[name] = slots |
- if clone: |
- return [x.cloneNode(deep) for x in slots] |
- return slots |
- |
- def getPattern(self, name, default=missingPattern, clone=1, deep=1): |
- """Get a named slot from the incoming template node. Returns a copy |
- of the node and all its children. If there was more than one node with |
- the same slot identifier, they will be returned in a round-robin fashion. |
- """ |
- slots = self.getAllPatterns(name, default=default, clone=0) |
- if slots is None: |
- return None |
- slot = slots.pop(0) |
- slots.append(slot) |
- if clone: |
- parentNode = slot.parentNode |
- slot.parentNode = None |
- clone = slot.cloneNode(deep) |
- if clone.attributes.has_key('pattern'): |
- del clone.attributes['pattern'] |
- elif clone.attributes.has_key(name + 'Of'): |
- del clone.attributes[name + 'Of'] |
- slot.parentNode = parentNode |
- if DEBUG: |
- clone.attributes['ofPattern'] = name + 'Of' |
- clone.attributes['nameOf'] = self.submodel.split('/')[-1] |
- return clone |
- if DEBUG: |
- slot.attributes['ofPattern'] = name + 'Of' |
- slot.attributes['nameOf'] = self.submodel.split('/')[-1] |
- return slot |
- |
- def addUpdateMethod(self, updateMethod): |
- """Add a method to this widget that will be called when the widget |
- is being rendered. The signature for this method should be |
- updateMethod(request, widget, data) where widget will be the |
- instance you are calling addUpdateMethod on. |
- """ |
- self.setupMethods.append(updateMethod) |
- |
- def addEventHandler(self, eventName, handler, *args): |
- """Add an event handler to this widget. eventName is a string |
- indicating which javascript event handler should cause this |
- handler to fire. Handler is a callable that has the signature |
- handler(request, widget, *args). |
- """ |
- def handlerUpdateStep(request, widget, data): |
- extraArgs = '' |
- for x in args: |
- extraArgs += " ,'" + x.replace("'", "\\'") + "'" |
- widget[eventName] = "return woven_eventHandler('%s', this%s)" % (eventName, extraArgs) |
- setattr(self, 'wevent_' + eventName, handler) |
- self.addUpdateMethod(handlerUpdateStep) |
- |
- def onEvent(self, request, eventName, *args): |
- """Dispatch a client-side event to an event handler that was |
- registered using addEventHandler. |
- """ |
- eventHandler = getattr(self, 'wevent_' + eventName, None) |
- if eventHandler is None: |
- raise NotImplementedError("A client side '%s' event occurred," |
- " but there was no event handler registered on %s." % |
- (eventName, self)) |
- |
- eventHandler(request, self, *args) |
- |
- |
-class DefaultWidget(Widget): |
- def generate(self, request, node): |
- """ |
- By default, we just return the node unchanged |
- """ |
- self.cleanNode(node) |
- if self.become: |
- become = self.become |
- self.become = None |
- parent = node.parentNode |
- node.parentNode = None |
- old = node.cloneNode(1) |
- node.parentNode = parent |
- gen = become.generateDOM(request, node) |
- del old.attributes['model'] |
- gen.appendChild(self.cleanNode(old)) |
- return gen |
- return node |
- |
- def modelChanged(self, payload): |
- """We're not concerned if the model has changed. |
- """ |
- pass |
- |
- |
-class Attributes(Widget): |
- """Set attributes on a node. |
- |
- Assumes model is a dictionary of attributes. |
- """ |
- |
- def setUp(self, request, node, data): |
- for k, v in data.items(): |
- self[k] = v |
- |
- |
-class Text(Widget): |
- """ |
- A simple Widget that renders some text. |
- """ |
- def __init__(self, model, raw=0, clear=1, *args, **kwargs): |
- """ |
- @param model: The text to render. |
- @type model: A string or L{model.Model}. |
- @param raw: A boolean that specifies whether to render the text as |
- a L{domhelpers.RawText} or as a DOM TextNode. |
- """ |
- self.raw = raw |
- self.clearNode = clear |
- Widget.__init__(self, model, *args, **kwargs) |
- |
- def generate(self, request, node): |
- if self.templateNode is None: |
- if self.raw: |
- return domhelpers.RawText(str(self.getData(request))) |
- else: |
- return document.createTextNode(str(self.getData(request))) |
- return Widget.generate(self, request, node) |
- |
- def setUp(self, request, node, data): |
- if self.raw: |
- textNode = domhelpers.RawText(str(data)) |
- else: |
- textNode = document.createTextNode(str(data)) |
- self.appendChild(textNode) |
- |
- |
-class ParagraphText(Widget): |
- """ |
- Like a normal text widget, but it takes line breaks in the text and |
- formats them as HTML paragraphs. |
- """ |
- def setUp(self, request, node, data): |
- nSplit = data.split('\n') |
- for line in nSplit: |
- if line.strip(): |
- para = request.d.createElement('p', caseInsensitive=0, preserveCase=0) |
- para.appendChild(request.d.createTextNode(line)) |
- self.add(para) |
- |
-class Image(Widget): |
- """ |
- A simple Widget that creates an `img' tag. |
- """ |
- tagName = 'img' |
- border = '0' |
- def setUp(self, request, node, data): |
- self['border'] = self.border |
- self['src'] = data |
- |
- |
-class Error(Widget): |
- tagName = 'span' |
- def __init__(self, model, message="", *args, **kwargs): |
- Widget.__init__(self, model, *args, **kwargs) |
- self.message = message |
- |
- def generateDOM(self, request, node): |
- self['style'] = 'color: red' |
- self.add(Text(" " + self.message)) |
- return Widget.generateDOM(self, request, node) |
- |
- |
-class Div(Widget): |
- tagName = 'div' |
- |
- |
-class Span(Widget): |
- tagName = 'span' |
- |
- |
-class Br(Widget): |
- tagName = 'br' |
- |
- |
-class Input(Widget): |
- tagName = 'input' |
- def setSubmodel(self, submodel): |
- self.submodel = submodel |
- self['name'] = submodel |
- |
- def setUp(self, request, node, data): |
- if not self.attributes.has_key('name') and not node.attributes.get('name'): |
- if self.submodel: |
- id = self.submodel |
- else: |
- id = self.attributes.get('id', node.attributes.get('id')) |
- self['name'] = id |
- if data is None: |
- data = '' |
- if not self.attributes.has_key('value'): |
- self['value'] = str(data) |
- |
- |
-class CheckBox(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'checkbox' |
- Input.setUp(self, request, node, data) |
- |
- |
-class RadioButton(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'radio' |
- Input.setUp(self, request, node, data) |
- |
- |
-class File(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'file' |
- Input.setUp(self, request, node, data) |
- |
- |
-class Hidden(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'hidden' |
- Input.setUp(self, request, node, data) |
- |
- |
-class InputText(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'text' |
- Input.setUp(self, request, node, data) |
- |
- |
-class PasswordText(Input): |
- """ |
- I render a password input field. |
- """ |
- def setUp(self, request, node, data): |
- self['type'] = 'password' |
- Input.setUp(self, request, node, data) |
- |
- |
-class Button(Input): |
- def setUp(self, request, node, data): |
- self['type'] = 'button' |
- Input.setUp(self, request, node, data) |
- |
- |
-class Select(Input): |
- tagName = 'select' |
- |
- |
-class Option(Widget): |
- tagName = 'option' |
- def initialize(self): |
- self.text = '' |
- |
- def setText(self, text): |
- """ |
- Set the text to be displayed within the select menu. |
- """ |
- self.text = text |
- |
- def setValue(self, value): |
- self['value'] = str(value) |
- |
- def setUp(self, request, node, data): |
- self.add(Text(self.text or data)) |
- if data is None: |
- data = '' |
- if not self.attributes.has_key('value'): |
- self['value'] = str(data) |
- |
-class Anchor(Widget): |
- tagName = 'a' |
- trailingSlash = '' |
- def initialize(self): |
- self.baseHREF = '' |
- self.parameters = {} |
- self.raw = 0 |
- self.text = '' |
- |
- def setRaw(self, raw): |
- self.raw = raw |
- |
- def setLink(self, href): |
- self.baseHREF= href |
- |
- def setParameter(self, key, value): |
- self.parameters[key] = value |
- |
- def setText(self, text): |
- self.text = text |
- |
- def setUp(self, request, node, data): |
- href = self.baseHREF |
- params = urllib.urlencode(self.parameters) |
- if params: |
- href = href + '?' + params |
- self['href'] = href or str(data) + self.trailingSlash |
- if data is None: |
- data = "" |
- self.add(Text(self.text or data, self.raw, 0)) |
- |
- |
-class SubAnchor(Anchor): |
- def initialize(self): |
- warnings.warn( |
- "SubAnchor is deprecated, you might want either Anchor or DirectoryAnchor", |
- DeprecationWarning) |
- Anchor.initialize(self) |
- |
- |
- |
-class DirectoryAnchor(Anchor): |
- trailingSlash = '/' |
- |
- |
-def appendModel(newNode, modelName): |
- if newNode is None: return |
- curModel = newNode.attributes.get('model') |
- if curModel is None: |
- newModel = str(modelName) |
- else: |
- newModel = '/'.join((curModel, str(modelName))) |
- newNode.attributes['model'] = newModel |
- |
- |
-class List(Widget): |
- """ |
- I am a widget which knows how to generateDOM for a python list. |
- |
- A List should be specified in the template HTML as so:: |
- |
- | <ul model="blah" view="List"> |
- | <li pattern="emptyList">This will be displayed if the list |
- | is empty.</li> |
- | <li pattern="listItem" view="Text">Foo</li> |
- | </ul> |
- |
- If you have nested lists, you may also do something like this:: |
- |
- | <table model="blah" view="List"> |
- | <tr pattern="listHeader"><th>A</th><th>B</th></tr> |
- | <tr pattern="emptyList"><td colspan='2'>***None***</td></tr> |
- | <tr pattern="listItem"> |
- | <td><span view="Text" model="1" /></td> |
- | <td><span view="Text" model="2" /></td> |
- | </tr> |
- | <tr pattern="listFooter"><td colspan="2">All done!</td></tr> |
- | </table> |
- |
- Where blah is the name of a list on the model; eg:: |
- |
- | self.model.blah = ['foo', 'bar'] |
- |
- """ |
- tagName = None |
- defaultItemView = "DefaultWidget" |
- def generateDOM(self, request, node): |
- node = Widget.generateDOM(self, request, node) |
- listHeaders = self.getAllPatterns('listHeader', None) |
- listFooters = self.getAllPatterns('listFooter', None) |
- emptyLists = self.getAllPatterns('emptyList', None) |
- domhelpers.clearNode(node) |
- if listHeaders: |
- node.childNodes.extend(listHeaders) |
- for n in listHeaders: n.parentNode = node |
- data = self.getData(request) |
- if data: |
- self._iterateData(node, self.submodel, data) |
- elif emptyLists: |
- node.childNodes.extend(emptyLists) |
- for n in emptyLists: n.parentNode = node |
- if listFooters: |
- node.childNodes.extend(listFooters) |
- for n in listFooters: n.parentNode = node |
- return node |
- |
- def _iterateData(self, parentNode, submodel, data): |
- currentListItem = 0 |
- retVal = [None] * len(data) |
- for itemNum in range(len(data)): |
- # theory: by appending copies of the li node |
- # each node will be handled once we exit from |
- # here because handleNode will then recurse into |
- # the newly appended nodes |
- |
- newNode = self.getPattern('listItem') |
- if newNode.getAttribute('model') == '.': |
- newNode.removeAttribute('model') |
- elif not newNode.attributes.get("view"): |
- newNode.attributes["view"] = self.defaultItemView |
- appendModel(newNode, itemNum) |
- retVal[itemNum] = newNode |
- newNode.parentNode = parentNode |
-# parentNode.appendChild(newNode) |
- parentNode.childNodes.extend(retVal) |
- |
- |
-class KeyedList(List): |
- """ |
- I am a widget which knows how to display the values stored within a |
- Python dictionary.. |
- |
- A KeyedList should be specified in the template HTML as so:: |
- |
- | <ul model="blah" view="KeyedList"> |
- | <li pattern="emptyList">This will be displayed if the list is |
- | empty.</li> |
- | <li pattern="keyedListItem" view="Text">Foo</li> |
- | </ul> |
- |
- I can take advantage of C{listHeader}, C{listFooter} and C{emptyList} |
- items just as a L{List} can. |
- """ |
- def _iterateData(self, parentNode, submodel, data): |
- """ |
- """ |
- currentListItem = 0 |
- keys = data.keys() |
- # Keys may be a tuple, if this is not a true dictionary but a dictionary-like object |
- if hasattr(keys, 'sort'): |
- keys.sort() |
- for key in keys: |
- newNode = self.getPattern('keyedListItem') |
- if not newNode: |
- newNode = self.getPattern('item', _RAISE) |
- if newNode: |
- warnings.warn("itemOf= is deprecated, " |
- "please use listItemOf instead", |
- DeprecationWarning) |
- |
- appendModel(newNode, key) |
- if not newNode.attributes.get("view"): |
- newNode.attributes["view"] = "DefaultWidget" |
- parentNode.appendChild(newNode) |
- |
- |
-class ColumnList(Widget): |
- def __init__(self, model, columns=1, start=0, end=0, *args, **kwargs): |
- Widget.__init__(self, model, *args, **kwargs) |
- self.columns = columns |
- self.start = start |
- self.end = end |
- |
- def setColumns(self, columns): |
- self.columns = columns |
- |
- def setStart(self, start): |
- self.start = start |
- |
- def setEnd(self, end): |
- self.end = end |
- |
- def setUp(self, request, node, data): |
- pattern = self.getPattern('columnListRow', clone=0) |
- if self.end: |
- listSize = self.end - self.start |
- if listSize > len(data): |
- listSize = len(data) |
- else: |
- listSize = len(data) |
- for itemNum in range(listSize): |
- if itemNum % self.columns == 0: |
- row = self.getPattern('columnListRow') |
- domhelpers.clearNode(row) |
- node.appendChild(row) |
- |
- newNode = self.getPattern('columnListItem') |
- |
- appendModel(newNode, itemNum + self.start) |
- if not newNode.attributes.get("view"): |
- newNode.attributes["view"] = "DefaultWidget" |
- row.appendChild(newNode) |
- node.removeChild(pattern) |
- return node |
- |
- |
-class Bold(Widget): |
- tagName = 'b' |
- |
- |
-class Table(Widget): |
- tagName = 'table' |
- |
- |
-class Row(Widget): |
- tagName = 'tr' |
- |
- |
-class Cell(Widget): |
- tagName = 'td' |
- |
- |
-class RawText(Widget): |
- def generateDOM(self, request, node): |
- self.node = domhelpers.RawText(self.getData(request)) |
- return self.node |
- |
-from types import StringType |
- |
-class Link(Widget): |
- """A utility class for generating <a href='foo'>bar</a> tags. |
- """ |
- tagName = 'a' |
- def setUp(self, request, node, data): |
- # TODO: we ought to support Deferreds here for both text and href! |
- if isinstance(data, StringType): |
- node.tagName = self.tagName |
- node.attributes["href"] = data |
- else: |
- data = self.model |
- txt = data.getSubmodel(request, "text").getData(request) |
- if not isinstance(txt, Node): |
- txt = document.createTextNode(txt) |
- lnk = data.getSubmodel(request, "href").getData(request) |
- self['href'] = lnk |
- node.tagName = self.tagName |
- domhelpers.clearNode(node) |
- node.appendChild(txt) |
- |
-class RootRelativeLink(Link): |
- """ |
- Just like a regular Link, only it makes the href relative to the |
- appRoot (that is, request.getRootURL()). |
- """ |
- def setUp(self, request, node, data): |
- # hack, hack: some juggling so I can type less and share more |
- # code with Link |
- st = isinstance(data, StringType) |
- if st: |
- data = request.getRootURL() + '/' + data |
- Link.setUp(self, request, node, data) |
- if not st: |
- self['href'] = request.getRootURL() + '/' + self['href'] |
- |
-class ExpandMacro(Widget): |
- """A Macro expansion widget modeled after the METAL expander |
- in ZPT/TAL/METAL. Usage: |
- |
- In the Page that is being rendered, place the ExpandMacro widget |
- on the node you want replaced with the Macro, and provide nodes |
- tagged with fill-slot= attributes which will fill slots in the Macro:: |
- |
- def wvfactory_myMacro(self, request, node, model): |
- return ExpandMacro( |
- model, |
- macroFile="MyMacro.html", |
- macroName="main") |
- |
- <div view="myMacro"> |
- <span fill-slot="greeting">Hello</span> |
- <span fill-slot="greetee">World</span> |
- </div> |
- |
- Then, in your Macro template file ("MyMacro.html" in the above |
- example) designate a node as the macro node, and nodes |
- inside that as the slot nodes:: |
- |
- <div macro="main"> |
- <h3><span slot="greeting" />, <span slot="greetee" />!</h3> |
- </div> |
- """ |
- def __init__(self, model, macroTemplate = "", macroFile="", macroFileDirectory="", macroName="", **kwargs): |
- self.macroTemplate = macroTemplate |
- self.macroFile=macroFile |
- self.macroFileDirectory=macroFileDirectory |
- self.macroName=macroName |
- Widget.__init__(self, model, **kwargs) |
- |
- def generate(self, request, node): |
- if self.macroTemplate: |
- templ = view.View( |
- self.model, |
- template = self.macroTemplate).lookupTemplate(request) |
- else: |
- templ = view.View( |
- self.model, |
- templateFile=self.macroFile, |
- templateDirectory=self.macroFileDirectory).lookupTemplate(request) |
- |
- ## We are going to return the macro node from the metatemplate, |
- ## after replacing any slot= nodes in it with fill-slot= nodes from `node' |
- macrolist = domhelpers.locateNodes(templ.childNodes, "macro", self.macroName) |
- assert len(macrolist) == 1, ("No macro or more than " |
- "one macro named %s found." % self.macroName) |
- |
- macro = macrolist[0] |
- del macro.attributes['macro'] |
- slots = domhelpers.findElementsWithAttributeShallow(macro, "slot") |
- for slot in slots: |
- slotName = slot.attributes.get("slot") |
- fillerlist = domhelpers.locateNodes(node.childNodes, "fill-slot", slotName) |
- assert len(fillerlist) <= 1, "More than one fill-slot found with name %s" % slotName |
- if len(fillerlist): |
- filler = fillerlist[0] |
- filler.tagName = filler.endTagName = slot.tagName |
- del filler.attributes['fill-slot'] |
- del slot.attributes['slot'] |
- filler.attributes.update(slot.attributes) |
- slot.parentNode.replaceChild(filler, slot) |
- |
- return macro |
- |
-class DeferredWidget(Widget): |
- def setDataCallback(self, result, request, node): |
- model = result |
- view = None |
- if isinstance(model, components.Componentized): |
- view = model.getAdapter(interfaces.IView) |
- if not view and hasattr(model, '__class__'): |
- view = interfaces.IView(model, None) |
- |
- if view: |
- view["id"] = self.attributes.get('id', '') |
- view.templateNode = node |
- view.controller = self.controller |
- return view.setDataCallback(result, request, node) |
- else: |
- return Widget.setDataCallback(self, result, request, node) |
- |
- |
-class Break(Widget): |
- """Break into pdb when this widget is rendered. Mildly |
- useful for debugging template structure, model stacks, |
- etc. |
- """ |
- def setUp(self, request, node, data): |
- import pdb; pdb.set_trace() |
- |
- |
-view.registerViewForModel(Text, model.StringModel) |
-view.registerViewForModel(List, model.ListModel) |
-view.registerViewForModel(KeyedList, model.DictionaryModel) |
-view.registerViewForModel(Link, model.Link) |
-view.registerViewForModel(DeferredWidget, model.DeferredWrapper) |