[silva.ui][Sylvain Viollon] Try to merge 2.4 branch into 3.1.

[email protected] Thu, 26 Sep 2013 14:33:46 +0200
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Thu Sep 26 14:33:42 2013 +0200
revision:  662:cd75455e3296 in silva.ui
branch:    3.1
details:   https://hg.infrae.com/silva.ui?cmd=changeset;node=cd75455e3296
modified:  src/silva/ui/icon.py src/silva/ui/interfaces.py src/silva/ui/layout_templates/defaultlayout.cpt src/silva/ui/rest/base.py src/silva/ui/rest/container/notifier.py src/silva/ui/rest/container/serializer.py src/silva/ui/rest/helper.py src/silva/ui/service.png src/silva/ui/service.py src/silva/ui/smi.py src/silva/ui/smi_templates/smitemplate.cpt src/silva/ui/static/css/forms.css src/silva/ui/static/css/forms.min.css src/silva/ui/static/css/smi.css src/silva/ui/static/css/smi.min.css src/silva/ui/static/img/minus-small.png src/silva/ui/static/img/plus-small.png src/silva/ui/static/img/settings.png src/silva/ui/static/img/sprite-local.png src/silva/ui/static/img/sprite.png src/silva/ui/static/js/infrae.js src/silva/ui/static/js/infrae.min.js src/silva/ui/static/js/infrae.ui.js src/sil
 va/ui/static/js/infrae.ui.min.js src/silva/ui/static/js/infrae.views.js src/silva/ui/static/js/infrae.views.min.js src/silva/ui/static/js/listing.items.js src/silva/ui/static/js/listing.item
 s.min.js src/silva/ui/static/js/smi.form.js src/silva/ui/static/js/smi.form.min.js src/silva/ui/static/js/smi.js src/silva/ui/static/js/smi.min.js src/silva/ui/static/js/smi.notification.js src/silva/ui/static/js/smi.notification.min.js src/silva/ui/static/js/smi.workspace.js src/silva/ui/static/js/smi.workspace.min.js src/silva/ui/tests/test_icon.py
added:     
removed:   src/silva/ui/static/img/minus-small.png src/silva/ui/static/img/plus-small.png src/silva/ui/static/img/settings.png src/silva/ui/static/img/sprite-local.png
log:       Try to merge 2.4 branch into 3.1.


diffstat:

 src/silva/ui/icon.py                            |   81 ++---
 src/silva/ui/interfaces.py                      |    7 +
 src/silva/ui/layout_templates/defaultlayout.cpt |    2 +-
 src/silva/ui/rest/base.py                       |   19 +-
 src/silva/ui/rest/container/notifier.py         |   11 +-
 src/silva/ui/rest/container/serializer.py       |    6 +-
 src/silva/ui/rest/helper.py                     |   62 ++--
 src/silva/ui/service.png                        |  Bin 
 src/silva/ui/service.py                         |    6 +
 src/silva/ui/smi.py                             |   17 +-
 src/silva/ui/smi_templates/smitemplate.cpt      |    3 +-
 src/silva/ui/static/css/forms.css               |   22 +-
 src/silva/ui/static/css/forms.min.css           |    2 +-
 src/silva/ui/static/css/smi.css                 |   12 +-
 src/silva/ui/static/css/smi.min.css             |    2 +-
 src/silva/ui/static/img/minus-small.png         |  Bin 
 src/silva/ui/static/img/plus-small.png          |  Bin 
 src/silva/ui/static/img/settings.png            |  Bin 
 src/silva/ui/static/img/sprite-local.png        |  Bin 
 src/silva/ui/static/img/sprite.png              |  Bin 
 src/silva/ui/static/js/infrae.js                |   12 +-
 src/silva/ui/static/js/infrae.min.js            |    2 +-
 src/silva/ui/static/js/infrae.ui.js             |    2 +-
 src/silva/ui/static/js/infrae.ui.min.js         |    2 +-
 src/silva/ui/static/js/infrae.views.js          |  186 +++++++++----
 src/silva/ui/static/js/infrae.views.min.js      |    2 +-
 src/silva/ui/static/js/listing.items.js         |    2 +-
 src/silva/ui/static/js/listing.items.min.js     |    2 +-
 src/silva/ui/static/js/smi.form.js              |  304 ++++++++++++++---------
 src/silva/ui/static/js/smi.form.min.js          |    2 +-
 src/silva/ui/static/js/smi.js                   |   14 +-
 src/silva/ui/static/js/smi.min.js               |    2 +-
 src/silva/ui/static/js/smi.notification.js      |   61 +++-
 src/silva/ui/static/js/smi.notification.min.js  |    2 +-
 src/silva/ui/static/js/smi.workspace.js         |   11 +-
 src/silva/ui/static/js/smi.workspace.min.js     |    2 +-
 src/silva/ui/tests/test_icon.py                 |   26 +-
 37 files changed, 562 insertions(+), 324 deletions(-)

diffs (1566 lines):

diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/icon.py
--- a/src/silva/ui/icon.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/icon.py	Thu Sep 26 14:33:42 2013 +0200
@@ -2,59 +2,46 @@
 # Copyright (c) 2011-2013 Infrae. All rights reserved.
 # See also LICENSE.txt
 
-
 from Products.Silva import icon
 from five import grok
 from silva.ui.interfaces import ISilvaUITheme
 
-ICON_SPRITE = {
-    'Silva AutoTOC': 'silva_autotoc',
-    'Silva CSV Source': 'silva_csvsource',
-    'Silva Document': 'silva_document',
-    'Silva Page': 'silva_page',
-    'Silva File': 'silva_file',
-    'Silva Find': 'silva_find',
-    'Silva Folder': 'silva_folder',
-    'Silva Ghost Folder': 'silva_ghostfolder',
-    'Silva Ghost': 'silva_ghost',
-    'Silva Image': 'silva_image',
-    'Silva Indexer': 'silva_indexer',
-    'Silva Link': 'silva_link',
-    'Silva Permanent Redirect Link': 'silva_permanentredirectlink',
-    'Silva Publication': 'silva_publication',
-    'Silva Root': 'silva_root',
-    'Silva Source Asset': 'silva_sourceasset',
-    'Silva Agenda Item': 'silva_agendaitem',
-    'Silva Agenda Page': 'silva_agendapage',
-    'Silva Agenda Filter': 'silva_agendafilter',
-    'Silva Agenda Viewer': 'silva_agendaviewer',
-    'Silva News Item': 'silva_newsitem',
-    'Silva News Page': 'silva_newspage',
-    'Silva News Filter': 'silva_newsfilter',
-    'Silva News Category Filter': 'silva_newscategoryfilter',
-    'Silva News Viewer': 'silva_newsviewer',
-    'Silva News Publication': 'silva_newspublication',
-    'Silva RSS Aggregator': 'silva_rssaggregator',
-    }
+
+ICON_SPRITE = icon.IconSprite(
+    sprite={
+        'Silva AutoTOC': 'silva_autotoc',
+        'Silva CSV Source': 'silva_csvsource',
+        'Silva Document': 'silva_document',
+        'Silva Page': 'silva_page',
+        'Silva File': 'silva_file',
+        'Silva Find': 'silva_find',
+        'Silva Folder': 'silva_folder',
+        'Silva Ghost Folder': 'silva_ghostfolder',
+        'Silva Ghost': 'silva_ghost',
+        'Silva Image': 'silva_image',
+        'Silva Indexer': 'silva_indexer',
+        'Silva Link': 'silva_link',
+        'Silva Permanent Redirect Link': 'silva_permanentredirectlink',
+        'Silva Publication': 'silva_publication',
+        'Silva Root': 'silva_root',
+        'Silva Source Asset': 'silva_sourceasset',
+        'Silva Agenda Item': 'silva_agendaitem',
+        'Silva Agenda Page': 'silva_agendapage',
+        'Silva Agenda Filter': 'silva_agendafilter',
+        'Silva Agenda Viewer': 'silva_agendaviewer',
+        'Silva News Item': 'silva_newsitem',
+        'Silva News Page': 'silva_newspage',
+        'Silva News Filter': 'silva_newsfilter',
+        'Silva News Category Filter': 'silva_newscategoryfilter',
+        'Silva News Viewer': 'silva_newsviewer',
+        'Silva News Publication': 'silva_newspublication',
+        'Silva RSS Aggregator': 'silva_rssaggregator',
+        },
+    url = lambda self, resolver, content: self.icon,
+    template = """<ins class="icon {url}"></ins>""")
 
 
 class SMIIconResolver(icon.IconResolver):
     grok.context(ISilvaUITheme)
 
-    def get_tag(self, content=None, identifier=None):
-        if content is not None:
-            identifier = getattr(content, 'meta_type', None)
-        if identifier in ICON_SPRITE:
-            return """<ins class="icon %s"></ins>""" % ICON_SPRITE[identifier]
-        return super(SMIIconResolver, self).get_tag(content, identifier)
-
-    def get_identifier_url(self, identifier):
-        if identifier in ICON_SPRITE:
-            return ICON_SPRITE[identifier]
-        return super(SMIIconResolver, self).get_identifier_url(identifier)
-
-    def get_content_url(self, content):
-        meta_type = getattr(content, 'meta_type', None)
-        if meta_type in ICON_SPRITE:
-            return ICON_SPRITE[meta_type]
-        return super(SMIIconResolver, self).get_content_url(content)
+    sprite = ICON_SPRITE
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/interfaces.py
--- a/src/silva/ui/interfaces.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/interfaces.py	Thu Sep 26 14:33:42 2013 +0200
@@ -166,6 +166,13 @@
                       u"resized to."),
         value_type=schema.Object(IPreviewResolution),
         required=True)
+    notifications_life = schema.Int(
+        title=_(u"Time notifications last"),
+        description=_(
+            u"Defines the time notifications messages last (in milliseconds). "
+            u"A SMI page refresh is needed after changing this."),
+        min=0,
+        required=False)
     smi_access_root = schema.Bool(
         title=_(u"SMI access Silva root"),
         description=_(
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/layout_templates/defaultlayout.cpt
--- a/src/silva/ui/layout_templates/defaultlayout.cpt	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/layout_templates/defaultlayout.cpt	Thu Sep 26 14:33:42 2013 +0200
@@ -9,7 +9,7 @@
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
     <meta name="apple-mobile-web-app-capable" content="yes" />
-    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
     <meta name="generator" content="Silva" />
     <title>Silva</title>
 
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/rest/base.py
--- a/src/silva/ui/rest/base.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/rest/base.py	Thu Sep 26 14:33:42 2013 +0200
@@ -15,7 +15,7 @@
 from infrae.rest.interfaces import IRESTComponent
 from silva.core.interfaces import IRoot, ISilvaObject, IVersion
 from silva.core.interfaces.adapters import IIconResolver
-from silva.core.services import task_queue
+from silva.core.services import CatalogingTask
 
 from ..interfaces import IUIScreen
 from ..menu import ContentMenu, ViewMenu, ActionMenu, UserMenu
@@ -54,7 +54,7 @@
         # Note: this will abort any previous changes.
         transaction.get().abort()
         self.transaction = transaction.begin()
-        task_queue.activate()
+        CatalogingTask.get().activate()
 
     def __exit__(self, t, v, tb):
         if v is None and not self.transaction.isDoomed():
@@ -122,7 +122,10 @@
         parents.reverse()
         return {'current': parents}
 
-    def get_metadata_menu(self, menu):
+    def get_menu_parent(self):
+        return {'path': self.get_content_path(aq_parent(self.context))}
+
+    def get_menu_entries(self, menu):
         entries = menu.get_entries(self.context).describe(self)
         if not entries:
             return None
@@ -142,15 +145,15 @@
                     self.request).get_content_url(self.context),
             },
             'menu': {
-                'content': self.get_metadata_menu(ContentMenu),
-                'view': self.get_metadata_menu(ViewMenu),
-                'actions': self.get_metadata_menu(ActionMenu),
-                'user': self.get_metadata_menu(UserMenu),
+                'content': self.get_menu_entries(ContentMenu),
+                'view': self.get_menu_entries(ViewMenu),
+                'actions': self.get_menu_entries(ActionMenu),
+                'user': self.get_menu_entries(UserMenu),
             },
             'path': self.get_content_path(self.context),
         }
         if not IRoot.providedBy(self.context):
-            metadata['up'] = self.get_content_path(aq_parent(self.context))
+            metadata['up'] = self.get_menu_parent()
         return {'content': {'ifaces': ['screen'],
                             'metadata': metadata,
                             'screen': screen},
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/rest/container/notifier.py
--- a/src/silva/ui/rest/container/notifier.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/rest/container/notifier.py	Thu Sep 26 14:33:42 2013 +0200
@@ -94,23 +94,22 @@
     """
 
     def __init__(self, request):
-        self.__send = getUtility(IMessageService).send
+        self._send = getUtility(IMessageService).send
         self.request = request
 
         adapter = IUserPreferredLanguages(self.request)
         languages = adapter.getPreferredLanguages()
         if languages:
-            self.__language = languages[0]
+            self._language = languages[0]
         else:
-            self.__language = 'en'
+            self._language = 'en'
 
     def translate(self, message):
         return translate(
-            message, target_language=self.__language, context=self.request)
-
+            message, target_language=self._language, context=self.request)
 
     def notify(self, message, type=u""):
-        self.__send(message, self.request, namespace=type)
+        self._send(message, self.request, namespace=type)
 
     @cofunction
     def __call__(self, parent, success_msg, failed_msg):
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/rest/container/serializer.py
--- a/src/silva/ui/rest/container/serializer.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/rest/container/serializer.py	Thu Sep 26 14:33:42 2013 +0200
@@ -11,6 +11,7 @@
 from silva.core.interfaces import IPublishable, INonPublishable
 from silva.core.interfaces import IVersionedObject
 from silva.core.interfaces.adapters import IIconResolver
+from silva.core.services.interfaces import IMemberService
 from silva.translations import translate as _
 
 from AccessControl import getSecurityManager
@@ -73,6 +74,9 @@
         locale = self.request.locale
         formatter = locale.dates.getFormatter('dateTime', 'short')
         self.format_date = lambda d: formatter.format(d.asdatetime())
+        self.format_author = lambda a: a.userid()
+        if getUtility(IMemberService).get_display_usernames():
+            self.format_author = lambda a: a.fullname()
 
     def get_access(self, content):
         for access, permission in [
@@ -95,7 +99,7 @@
         if previewable is not None:
             icon = self.get_icon(content)
             title = cgi.escape(previewable.get_title_or_id_editable())
-            author = previewable.get_last_author_info().userid()
+            author = self.format_author(previewable.get_last_author_info())
             modified = self.get_metadata(
                 previewable, 'silva-extra', 'modificationtime')
             if modified is None:
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/rest/helper.py
--- a/src/silva/ui/rest/helper.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/rest/helper.py	Thu Sep 26 14:33:42 2013 +0200
@@ -2,19 +2,19 @@
 # Copyright (c) 2012-2013 Infrae. All rights reserved.
 # See also LICENSE.txt
 
+from AccessControl.security import checkPermission
+
 from collections import defaultdict
 
 from five import grok
-from zope.cachedescriptors.property import CachedProperty
+from zope.cachedescriptors.property import Lazy
 from zope.component import getUtility, getMultiAdapter
 from zope.i18n import translate
 from zope.i18n.interfaces import IUserPreferredLanguages
 from zope.interface import Interface
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 
-import fanstatic
-from fanstatic.core import bundle_resources
-
+from silva.fanstatic import get_inclusion, get_needed
 from silva.core.messages.interfaces import IMessageService
 from silva.core.views.interfaces import IVirtualSite, IContentURL
 from silva.core.references.utils import relative_path
@@ -31,15 +31,6 @@
         super(UIHelper, self).__init__(context, request)
         self.context = context
         self.request = request
-        site = IVirtualSite(request)
-        settings = getUtility(IUIService)
-        if settings.smi_access_root:
-            root = site.get_silva_root()
-        else:
-            root = site.get_root()
-        url = getMultiAdapter((root, request), IContentURL)
-        self.root_path = url.url(relative=True)
-        self.root_url = url.url()
         self._providers = []
 
     def need(self, provider):
@@ -49,7 +40,35 @@
         return self._providers + grok.queryMultiSubscriptions(
             (self, self.request), IUIPlugin)
 
-    @CachedProperty
+    def _get_root_content_url(self):
+        # Redirect to the root of the SMI if we are not already
+        site = IVirtualSite(self.request)
+        settings = getUtility(IUIService)
+        if settings.smi_access_root:
+            top_level = site.get_silva_root()
+        else:
+            top_level = site.get_root()
+
+        # We lookup for the highest container where we have access
+        root = self.context.get_container()
+        while root != top_level:
+            parent = root.get_real_container()
+            if (parent is None or
+                not checkPermission('silva.ReadSilvaContent', parent)):
+                # We don't have access at that level
+                break
+            root = parent
+        return getMultiAdapter((root, self.request), IContentURL)
+
+    @Lazy
+    def root_url(self):
+        return self._get_root_content_url().url()
+
+    @Lazy
+    def root_path(self):
+        return self._get_root_content_url().url(relative=True)
+
+    @Lazy
     def language(self):
         adapter = IUserPreferredLanguages(self.request)
         languages = adapter.getPreferredLanguages()
@@ -74,8 +93,6 @@
     for message in service.receive_all(helper.request):
         info = {'message': helper.translate(message.content),
                 'category': message.namespace}
-        if message.namespace != 'error':
-           info['autoclose'] = 4000
         messages.append(info)
     if not messages:
         return
@@ -105,21 +122,14 @@
         self.request = request
 
     def __call__(self, screen, data):
-        needed = fanstatic.get_needed()
+        needed = get_needed()
         if not needed.has_resources():
             return
         if not needed.has_base_url():
             needed.set_base_url(screen.root_url)
         urls = defaultdict(list)
-        cache = {}
-        resources = needed.resources()
-        if needed._bundle:
-            resources = bundle_resources(resources)
-        for resource in resources:
-            library = resource.library
-            library_url = cache.get(library.name)
-            if library_url is None:
-                library_url = cache[library.name] = needed.library_url(library)
+        for resource in get_inclusion(needed).resources:
+            library_url = needed.library_url(resource.library)
             resource_url =  '/'.join((library_url, resource.relpath))
             urls[resource.ext[1:]].append(resource_url)
 
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/service.png
Binary file src/silva/ui/service.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/service.py
--- a/src/silva/ui/service.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/service.py	Thu Sep 26 14:33:42 2013 +0200
@@ -50,6 +50,7 @@
     public_url = None
     preview_url = None
     preview_use_resolutions = True
+    notifications_life = 4000
     preview_resolutions = [
         PreviewResolution('Fullscreen', None),
         PreviewResolution('Small', '800x600'),
@@ -93,7 +94,12 @@
         preview_url = data['preview_url']
         preview_use_resolutions = data['preview_use_resolutions']
         preview_resolutions = data['preview_resolutions']
+        notifications_life = data['notifications_life']
         smi_access_root = data['smi_access_root']
+        if notifications_life is not silvaforms.NO_VALUE:
+            self.context.notifications_life = notifications_life
+        else:
+            self.context.notifications_life = 4000
         if background != silvaforms.NO_VALUE:
             self.context.background = background
         else:
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/smi.py
--- a/src/silva/ui/smi.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/smi.py	Thu Sep 26 14:33:42 2013 +0200
@@ -23,6 +23,7 @@
 from zope.pagetemplate.interfaces import IPageTemplate
 
 from AccessControl import getSecurityManager
+from AccessControl.security import checkPermission
 from zExceptions import Redirect
 
 
@@ -46,9 +47,20 @@
         site = IVirtualSite(self.request)
         settings = getUtility(IUIService)
         if settings.smi_access_root:
-            root = site.get_silva_root()
+            top_level = site.get_silva_root()
         else:
-            root = site.get_root()
+            top_level = site.get_root()
+
+        # We lookup for the highest container where we have access
+        root = self.context.get_container()
+        while root != top_level:
+            parent = root.get_real_container()
+            if (parent is None or
+                    not checkPermission('silva.ReadSilvaContent', parent)):
+                # We don't have access at that level
+                break
+            root = parent
+
         root_content_url = getMultiAdapter((root, self.request), IContentURL)
         root_url = root_content_url.url()
         if root != self.context:
@@ -81,6 +93,7 @@
         self.maintenance_message = settings.maintenance_message
         self.test_mode = settings.test_mode
         self.preview_resolutions = []
+        self.notifications_life = settings.notifications_life
         if settings.preview_use_resolutions:
             self.preview_resolutions = list(settings.preview_resolutions)
         if settings.background:
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/smi_templates/smitemplate.cpt
--- a/src/silva/ui/smi_templates/smitemplate.cpt	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/smi_templates/smitemplate.cpt	Thu Sep 26 14:33:42 2013 +0200
@@ -8,7 +8,7 @@
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
     <meta name="apple-mobile-web-app-capable" content="yes" />
-    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
     <meta name="generator" content="Silva" />
     <title tal:content="view.name">Silva</title>
 
@@ -159,6 +159,7 @@
               speed: 0,
               standard: true,
 </tal:test>
+              life: "${view.notifications_life}",
               selector: "#notifications",
               url: "${view.root_url}/++rest++silva.ui.poll.notifications",
           },
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/css/forms.css
--- a/src/silva/ui/static/css/forms.css	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/css/forms.css	Thu Sep 26 14:33:42 2013 +0200
@@ -179,7 +179,7 @@
 }
 
 input[type="submit"] {
-  background: url("../img/gradient20m.png") repeat-x scroll center top #FFFFFF;
+  background: url("../img/gradient20m.png") repeat-x scroll center bottom #FFFFFF;
   color: #000;
   margin: 8px 2px 0 0;
 }
@@ -209,33 +209,33 @@
 
 input.field-collection-add-line {
   background-color: #F1F1F1;
-  background-image: url("../img/plus-small.png");
+  background-image: url("../img/sprite.png");
   background-repeat: no-repeat;
-  background-position: 3px 1px;
+  background-position: 0 -181px;
   padding: 0 0 0 17px;
 }
 
 input.field-collection-add-line:hover {
   background-color: #FFF;
-  background-image: url("../img/plus-small.png");
+  background-image: url("../img/sprite.png");
   background-repeat: no-repeat;
-  background-position: 3px 1px;
+  background-position: 0 -181px;
   padding: 0 0 0 17px;
 }
 
 input.field-collection-remove-line {
   background-color: #F1F1F1;
-  background-image: url("../img/minus-small.png");
+  background-image: url("../img/sprite.png");
   background-repeat: no-repeat;
-  background-position: 3px 1px;
+  background-position: 0 -161px;
   padding: 0 0 0 17px;
 }
 
 input.field-collection-remove-line:hover {
   background-color: #FFF;
-  background-image: url("../img/minus-small.png");
+  background-image: url("../img/sprite.png");
   background-repeat: no-repeat;
-  background-position: 3px 1px;
+  background-position: 0 -161px;
   padding: 0 0 0 17px;
 }
 
@@ -424,7 +424,7 @@
   -webkit-border-radius:3px;
   -moz-border-radius:3px;
   border-radius: 3px;
-  background: #fff url(../img/gradient20m.png) repeat-x top;
+  background: #fff url(../img/gradient20m.png) repeat-x bottom;
   border: 1px #777 solid;
   float: left;
   margin: 5px 0 0 6px;
@@ -529,7 +529,7 @@
   -webkit-border-radius:3px;
   -moz-border-radius:3px;
   border-radius: 3px;
-  background: #fff url(../img/gradient20m.png) repeat-x top;
+  background: #fff url(../img/gradient20m.png) repeat-x bottom;
   border: 1px #777 solid;
   float: left;
 }
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/css/forms.min.css
--- a/src/silva/ui/static/css/forms.min.css	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/css/forms.min.css	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-form{color:#000;clear:both}.forms{position:absolute;top:0;left:0;right:250px}#form{margin:0 5px}#workspace .form-content h3{border-bottom:1px solid #222;color:#222;font-weight:400;font-size:125%;margin:15px 5px 10px 5px}#workspace .form-content h4{background-color:#EFEFEF;background-image:-webkit-gradient(linear,left top,left bottom,from(#F9F9F9),to(#EFEFEF));background-image:-moz-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:-o-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:-ms-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:linear-gradient(top,#F9F9F9,#EFEFEF);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#000;font-size:110%;font-weight:400;margin:5px;padding:1px 6px 0 6px}#workspace .form-content #form-user
 role h4,#workspace .form-content .form-fields-container h4{margin:5px 0}#workspace .form-content h5{background-color:#999;background-image:-webkit-gradient(linear,left top,left bottom,from(#
 999),to(#777));background-image:-moz-linear-gradient(top,#999,#777);background-image:-o-linear-gradient(top,#999,#777);background-image:-ms-linear-gradient(top,#999,#777);background-image:-moz-linear-gradient(top,#999,#777);background-image:linear-gradient(top,#999,#777);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#FFF;font-size:110%;margin:5px 5px 0 5px;padding:2px 6px 0 6px}.form-content .form-head p{color:#000;padding:2px 5px}form .form-label{display:block;float:left;margin-right:25px;vertical-align:top;width:225px}.form-content .form-section .form-label label,.form-content .form-section .form-checkbox label{font-weight:700;font-size:105%}.form-content .form-section .form-label p,.form-content .form-section .form-checkbox p{f
 ont-size:90%;color:#777;margin-top:3px}.form-content .form-focus .form-label p,.form-content .form-focus .form-checkbox p{color:#000;margin-top:3px}.form-content .form-section:hover .form-la
 bel p,.form-content .form-section:hover .form-checkbox p{color:#000}.form-content .form-field{display:block;float:left}#form-metadataformgroup .form-content .form-field .field{width:325px}#form-metadataformgroup .form-content .form-field .field:hover{border-color:#222;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.form-field .field:focus{border:1px solid #444;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}#form #approval-history .form-label{float:none;padding:3px;width:100%}#form #approval-history .form-content .form-section{margin:5px;padding:0 5px 25px}#form #approval-history .form-content .form-batch{border-style:none}#form #approval-history .form-content .form-section .form-label label{display:block}#form #approval-history .form-content ol{list
 -style-type:none;margin-left:0}#form #approval-history .label{font-weight:700}#form #approval-history .form-field{padding:3px}#form #approval-history div.batching a{text-decoration:underline
 }input,textarea,select{border:1px solid #777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:2px}input[type="submit"]{background:url(../img/gradient20m.png) repeat-x scroll center top #FFF;color:#000;margin:8px 2px 0 0}input[type="submit"]:hover{background:#FFF}input:focus,textarea:focus{border:1px solid #000;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:2px 2px 3px #A0A0A0;-moz-box-shadow:2px 2px 3px #A0A0A0;box-shadow:2px 2px 3px #A0A0A0}.form-checkbox input{margin:0 4px 4px 0}.form-content .form-batch{border-left:2px solid #CCC;padding:3px 6px}input.field-collection-add-line{background-color:#F1F1F1;background-image:url(../img/plus-small.png);background-repeat:no-repeat;background-position:3px 1px;padding:0 0 0 17px}inp
 ut.field-collection-add-line:hover{background-color:#FFF;background-image:url(../img/plus-small.png);background-repeat:no-repeat;background-position:3px 1px;padding:0 0 0 17px}input.field-co
 llection-remove-line{background-color:#F1F1F1;background-image:url(../img/minus-small.png);background-repeat:no-repeat;background-position:3px 1px;padding:0 0 0 17px}input.field-collection-remove-line:hover{background-color:#FFF;background-image:url(../img/minus-small.png);background-repeat:no-repeat;background-position:3px 1px;padding:0 0 0 17px}#form-metadatformgroup .form-content{margin-bottom:25px}.form-content .form-body .form-focus{border-left:2px solid rgba(0,0,0,.6);padding-left:7px}.form-content .form-section{padding:10px 8px;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin:3px 5px}.form-fields-container{margin:0 5px 20px 5px}form .form-footer{border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;
 border-radius:3px;height:30px;margin:0 5px}#workspace .form-content .form-table th,#workspace .form-content .form-table td{text-align:left;padding:2px}#workspace .form-content .form-table .f
 orm-controls td{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#FFF),to(#F9F9F9));background-image:-moz-linear-gradient(top,#FFF,#F9F9F9);background-image:-o-linear-gradient(top,#FFF,#F9F9F9);background-image:-ms-linear-gradient(top,#FFF,#F9F9F9);background-image:-moz-linear-gradient(top,#FFF,#F9F9F9);background-image:linear-gradient(top,#fff,#F9F9F9);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#workspace .form-content .form-table tr.form-controls td a{margin:3px 0 3px 4px}#workspace .form-table .footer-fields{float:left}#workspace .form-table .footer-fields label,#workspace .form-table .footer-fields .required{vertical-align:middle;margin-left:3px}#workspace .form-content .form-table selec
 t{margin-right:5px}.form-content .form-required,.form-content .form-acquirable,.form-acquired-label,span.required{border:1px solid rgba(0,0,0,.1);-webkit-border-radius:4px;-webkit-border-rad
 ius:4px;border-radius:4px;color:#FFF;display:inline-block;font-size:9px;font-weight:700;height:10px;text-align:center;text-transform:capitalize;vertical-align:top}.form-content .form-required,.form-content .form-acquirable{float:right;width:60px}span.required{width:60px}.form-content .form-required,span.required{background-color:#de2a2a}.form-content .form-acquirable{background-color:#faa937;margin-top:4px}.form-acquired-label{background:#46a545;width:80px}p.form-acquired{color:#777;font-size:95%;margin-top:4px;width:325px}.form-section .form-focus p.form-acquired,.form-section:hover p.form-acquired{color:#000}form .form-error-message{position:relative;float:left}.form-content .form-error{border-left:3px solid #DE2A2A;padding-left:6px}.form-content .form-error-detail{background:#FFF5F5;bo
 rder:1px solid #DE2A2A;-webkit-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;color:#DE2A2A;display:block;padding:4px;position:absolute;left:18px;width:225px;z-index:150;-webk
 it-box-shadow:2px 2px 3px #A0A0A0;-moz-box-shadow:2px 2px 3px #A0A0A0;box-shadow:2px 2px 3px #A0A0A0}.form-content .form-error-detail:before{content:' ';border:5px solid;border-color:transparent #DE2A2A transparent transparent;position:absolute;left:-11px;top:6px;height:0;width:0}.form-content .form-error-detail:after{content:' ';border:5px solid;border-color:transparent #FFF5F5 transparent transparent;position:absolute;left:-10px;top:6px;height:0;width:0}.form-content a.form-control,.form-content a.form-button{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff url(../img/gradient20m.png) repeat-x top;border:1px #777 solid;float:left;margin:5px 0 0 6px;padding:1px 5px}.form-content a.form-control:hover,.form-content a.form-button:hover{background-color:#FFF
 ;background-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA
 ,#FFF);background-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF)}.form-content a.form-control ins,.form-content a.form-button ins{float:left}#workspace .header .tabs li.top-level:hover{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA,#FFF);background-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF);color:#000}.ui-dialog .form-fields-container .form-content .form-section .form-field{display:block;position:relative}.ui-dialog .f
 orm-fields-container .form-content .form-required{float:right}.ui-dialog .form-fields-container .form-content .form-section .form-label{display:block;float:left}.ui-dialog .form-fields-conta
 iner .form-content .form-section .form-label label{display:inline;margin:0;float:left}.ui-dialog .form-fields-container .form-content .form-section .form-error-message{float:left;position:relative}.ui-dialog .ui-widget div.actions{background:#FFF;border:1px solid #FFF}.ui-dialog .ui-widget div.actions .last-action{margin-left:10px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.ui-dialog #form-field-content{padding:5px}.ui-dialog #form-field-content a.form-button{margin:8px 0 3px 3px}.ui-dialog .form-section .form-label{margin-right:20px;width:300px}.ui-dialog .form-section .form-label p{clear:left}.ui-dialog .ui-dialog-buttonpane button,.ui-dialog .ui-widget div.actions .last-action{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff url
 (../img/gradient20m.png) repeat-x top;border:1px #777 solid;float:left}.ui-dialog .ui-dialog-buttonpane button{margin:5px 0 0 6px;padding:1px 5px}.ui-dialog .ui-widget div.actions .last-acti
 on :hover,.ui-dialog .ui-dialog-buttonpane button:hover{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA,#FFF);background-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF)}.ui-dialog .ui-dialog-buttonpane button .ui-button-text{padding:2px}.form-content input.field-collection-line-selector{float:right;height:20px;width:20px;border:0;margin-right:6px}.form-content tr.field-collection-line input.field-textline,.form-content tr.field-collection-line table.field-collection-header{width:100%}.form-content div.field-collection-line{cle
 ar:both}.form-content div.field-collection-line div.field-object{margin:15px;clear:both}.form-content .field-collection-lines div.line-actions{float:left;width:60px}div.line-data{float:left;
 margin-bottom:5px}.form-content .field-collection-lines td.line-actions{width:20px}.form-content .field-collection-lines td.ordering-actions{width:60px}.form-content button.field-collection-move-up{background-image:url(../img/sprite.png);background-position:-80px -122px!important;background-color:transparent;height:20px;width:20px;border:0;float:left}.form-content button.field-collection-move-down{background-image:url(../img/sprite.png);background-position:-100px -122px!important;background-color:transparent;height:20px;width:20px;border:0;float:right}.form-content .field-collection .multi-actions{clear:both}.form-content a.field-uri{text-decoration:underline}.portlets{position:absolute;top:0;right:5px;width:250px;bottom:0}#workspace .form-content .portlet h4{background:transparent;border
 -style:solid;border-width:0 0 1px 0;border-color:#111;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;display:inline-block;padding:0}@media screen and (max-width: 650px){.portle
 ts{ display:none}.forms{right:0}}.portlets p{margin:0 5px}.portlets .external-controls{padding:5px 20px 32px 5px}#cke_dialog_contents_46 .cke_dialog_ui_input_text{padding:0}table.cke_dialog_contents td.cke_dialog_ui_vbox_child input.cke_dialog_ui_input_text{height:24px}.cke_skin_silva div.cke_dialog_ui_input_text,.cke_skin_silva div.cke_dialog_ui_input_password,.cke_skin_silvadiv.cke_dialog_ui_input_textarea{padding:0!important}.portlets p span.orientation{display:block;margin-top:.3em;border:1px dotted black;text-align:center}.portlets p span.portrait{width:4.5em;height:6em}.portlets p span.landscape{width:6.5em;height:3.5em}.portlets p span.square{width:4em;height:4em}.portlets .references .item{clear:both;margin:0 5px}.portlets .references .item a.edit-screen{float:right;margin-right:5
 px;cursor:pointer}.portlets .references .item p{margin-left:20px}.portlets .references .item img{float:left;margin:5px;cursor:inherit}
\ No newline at end of file
+form{color:#000;clear:both}.forms{position:absolute;top:0;left:0;right:250px}#form{margin:0 5px}#workspace .form-content h3{border-bottom:1px solid #222;color:#222;font-weight:400;font-size:125%;margin:15px 5px 10px 5px}#workspace .form-content h4{background-color:#EFEFEF;background-image:-webkit-gradient(linear,left top,left bottom,from(#F9F9F9),to(#EFEFEF));background-image:-moz-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:-o-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:-ms-linear-gradient(top,#F9F9F9,#EFEFEF);background-image:linear-gradient(top,#F9F9F9,#EFEFEF);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#000;font-size:110%;font-weight:400;margin:5px;padding:1px 6px 0 6px}#workspace .form-content #form-user
 role h4,#workspace .form-content .form-fields-container h4{margin:5px 0}#workspace .form-content h5{background-color:#999;background-image:-webkit-gradient(linear,left top,left bottom,from(#
 999),to(#777));background-image:-moz-linear-gradient(top,#999,#777);background-image:-o-linear-gradient(top,#999,#777);background-image:-ms-linear-gradient(top,#999,#777);background-image:-moz-linear-gradient(top,#999,#777);background-image:linear-gradient(top,#999,#777);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#FFF;font-size:110%;margin:5px 5px 0 5px;padding:2px 6px 0 6px}.form-content .form-head p{color:#000;padding:2px 5px}form .form-label{display:block;float:left;margin-right:25px;vertical-align:top;width:225px}.form-content .form-section .form-label label,.form-content .form-section .form-checkbox label{font-weight:700;font-size:105%}.form-content .form-section .form-label p,.form-content .form-section .form-checkbox p{f
 ont-size:90%;color:#777;margin-top:3px}.form-content .form-focus .form-label p,.form-content .form-focus .form-checkbox p{color:#000;margin-top:3px}.form-content .form-section:hover .form-la
 bel p,.form-content .form-section:hover .form-checkbox p{color:#000}.form-content .form-field{display:block;float:left}#form-metadataformgroup .form-content .form-field .field{width:325px}#form-metadataformgroup .form-content .form-field .field:hover{border-color:#222;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.form-field .field:focus{border:1px solid #444;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}#form #approval-history .form-label{float:none;padding:3px;width:100%}#form #approval-history .form-content .form-section{margin:5px;padding:0 5px 25px}#form #approval-history .form-content .form-batch{border-style:none}#form #approval-history .form-content .form-section .form-label label{display:block}#form #approval-history .form-content ol{list
 -style-type:none;margin-left:0}#form #approval-history .label{font-weight:700}#form #approval-history .form-field{padding:3px}#form #approval-history div.batching a{text-decoration:underline
 }input,textarea,select{border:1px solid #777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:2px}input[type="submit"]{background:url(../img/gradient20m.png) repeat-x scroll center bottom #FFF;color:#000;margin:8px 2px 0 0}input[type="submit"]:hover{background:#FFF}input:focus,textarea:focus{border:1px solid #000;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:2px 2px 3px #A0A0A0;-moz-box-shadow:2px 2px 3px #A0A0A0;box-shadow:2px 2px 3px #A0A0A0}.form-checkbox input{margin:0 4px 4px 0}.form-content .form-batch{border-left:2px solid #CCC;padding:3px 6px}input.field-collection-add-line{background-color:#F1F1F1;background-image:url(../img/sprite.png);background-repeat:no-repeat;background-position:0 -181px;padding:0 0 0 17px}inp
 ut.field-collection-add-line:hover{background-color:#FFF;background-image:url(../img/sprite.png);background-repeat:no-repeat;background-position:0 -181px;padding:0 0 0 17px}input.field-colle
 ction-remove-line{background-color:#F1F1F1;background-image:url(../img/sprite.png);background-repeat:no-repeat;background-position:0 -161px;padding:0 0 0 17px}input.field-collection-remove-line:hover{background-color:#FFF;background-image:url(../img/sprite.png);background-repeat:no-repeat;background-position:0 -161px;padding:0 0 0 17px}#form-metadatformgroup .form-content{margin-bottom:25px}.form-content .form-body .form-focus{border-left:2px solid rgba(0,0,0,.6);padding-left:7px}.form-content .form-section{padding:10px 8px;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin:3px 5px}.form-fields-container{margin:0 5px 20px 5px}form .form-footer{border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-moz-border-radius:3px;border-radi
 us:3px;height:30px;margin:0 5px}#workspace .form-content .form-table th,#workspace .form-content .form-table td{text-align:left;padding:2px}#workspace .form-content .form-table .form-control
 s td{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#FFF),to(#F9F9F9));background-image:-moz-linear-gradient(top,#FFF,#F9F9F9);background-image:-o-linear-gradient(top,#FFF,#F9F9F9);background-image:-ms-linear-gradient(top,#FFF,#F9F9F9);background-image:-moz-linear-gradient(top,#FFF,#F9F9F9);background-image:linear-gradient(top,#fff,#F9F9F9);border:1px solid rgba(0,0,0,.2);-webkit-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#workspace .form-content .form-table tr.form-controls td a{margin:3px 0 3px 4px}#workspace .form-table .footer-fields{float:left}#workspace .form-table .footer-fields label,#workspace .form-table .footer-fields .required{vertical-align:middle;margin-left:3px}#workspace .form-content .form-table select{margin-ri
 ght:5px}.form-content .form-required,.form-content .form-acquirable,.form-acquired-label,span.required{border:1px solid rgba(0,0,0,.1);-webkit-border-radius:4px;-webkit-border-radius:4px;bor
 der-radius:4px;color:#FFF;display:inline-block;font-size:9px;font-weight:700;height:10px;text-align:center;text-transform:capitalize;vertical-align:top}.form-content .form-required,.form-content .form-acquirable{float:right;width:60px}span.required{width:60px}.form-content .form-required,span.required{background-color:#de2a2a}.form-content .form-acquirable{background-color:#faa937;margin-top:4px}.form-acquired-label{background:#46a545;width:80px}p.form-acquired{color:#777;font-size:95%;margin-top:4px;width:325px}.form-section .form-focus p.form-acquired,.form-section:hover p.form-acquired{color:#000}form .form-error-message{position:relative;float:left}.form-content .form-error{border-left:3px solid #DE2A2A;padding-left:6px}.form-content .form-error-detail{background:#FFF5F5;border:1px so
 lid #DE2A2A;-webkit-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;color:#DE2A2A;display:block;padding:4px;position:absolute;left:18px;width:225px;z-index:150;-webkit-box-shad
 ow:2px 2px 3px #A0A0A0;-moz-box-shadow:2px 2px 3px #A0A0A0;box-shadow:2px 2px 3px #A0A0A0}.form-content .form-error-detail:before{content:' ';border:5px solid;border-color:transparent #DE2A2A transparent transparent;position:absolute;left:-11px;top:6px;height:0;width:0}.form-content .form-error-detail:after{content:' ';border:5px solid;border-color:transparent #FFF5F5 transparent transparent;position:absolute;left:-10px;top:6px;height:0;width:0}.form-content a.form-control,.form-content a.form-button{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff url(../img/gradient20m.png) repeat-x bottom;border:1px #777 solid;float:left;margin:5px 0 0 6px;padding:1px 5px}.form-content a.form-control:hover,.form-content a.form-button:hover{background-color:#FFF;backgro
 und-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA,#FFF);b
 ackground-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF)}.form-content a.form-control ins,.form-content a.form-button ins{float:left}#workspace .header .tabs li.top-level:hover{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA,#FFF);background-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF);color:#000}.ui-dialog .form-fields-container .form-content .form-section .form-field{display:block;position:relative}.ui-dialog .form-fiel
 ds-container .form-content .form-required{float:right}.ui-dialog .form-fields-container .form-content .form-section .form-label{display:block;float:left}.ui-dialog .form-fields-container .fo
 rm-content .form-section .form-label label{display:inline;margin:0;float:left}.ui-dialog .form-fields-container .form-content .form-section .form-error-message{float:left;position:relative}.ui-dialog .ui-widget div.actions{background:#FFF;border:1px solid #FFF}.ui-dialog .ui-widget div.actions .last-action{margin-left:10px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.ui-dialog #form-field-content{padding:5px}.ui-dialog #form-field-content a.form-button{margin:8px 0 3px 3px}.ui-dialog .form-section .form-label{margin-right:20px;width:300px}.ui-dialog .form-section .form-label p{clear:left}.ui-dialog .ui-dialog-buttonpane button,.ui-dialog .ui-widget div.actions .last-action{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff url(../img/
 gradient20m.png) repeat-x bottom;border:1px #777 solid;float:left}.ui-dialog .ui-dialog-buttonpane button{margin:5px 0 0 6px;padding:1px 5px}.ui-dialog .ui-widget div.actions .last-action :h
 over,.ui-dialog .ui-dialog-buttonpane button:hover{background-color:#FFF;background-image:-webkit-gradient(linear,left top,left bottom,from(#EAEAEA),to(#FFF));background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:-o-linear-gradient(top,#EAEAEA,#FFF);background-image:-ms-linear-gradient(top,#EAEAEA,#FFF);background-image:-moz-linear-gradient(top,#EAEAEA,#FFF);background-image:linear-gradient(top,#EAEAEA,#FFF)}.ui-dialog .ui-dialog-buttonpane button .ui-button-text{padding:2px}.form-content input.field-collection-line-selector{float:right;height:20px;width:20px;border:0;margin-right:6px}.form-content tr.field-collection-line input.field-textline,.form-content tr.field-collection-line table.field-collection-header{width:100%}.form-content div.field-collection-line{clear:bo
 th}.form-content div.field-collection-line div.field-object{margin:15px;clear:both}.form-content .field-collection-lines div.line-actions{float:left;width:60px}div.line-data{float:left;margi
 n-bottom:5px}.form-content .field-collection-lines td.line-actions{width:20px}.form-content .field-collection-lines td.ordering-actions{width:60px}.form-content button.field-collection-move-up{background-image:url(../img/sprite.png);background-position:-80px -122px!important;background-color:transparent;height:20px;width:20px;border:0;float:left}.form-content button.field-collection-move-down{background-image:url(../img/sprite.png);background-position:-100px -122px!important;background-color:transparent;height:20px;width:20px;border:0;float:right}.form-content .field-collection .multi-actions{clear:both}.form-content a.field-uri{text-decoration:underline}.portlets{position:absolute;top:0;right:5px;width:250px;bottom:0}#workspace .form-content .portlet h4{background:transparent;border-styl
 e:solid;border-width:0 0 1px 0;border-color:#111;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;display:inline-block;padding:0}@media screen and (max-width: 650px){.portlets{ d
 isplay:none}.forms{right:0}}.portlets p{margin:0 5px}.portlets .external-controls{padding:5px 20px 32px 5px}#cke_dialog_contents_46 .cke_dialog_ui_input_text{padding:0}table.cke_dialog_contents td.cke_dialog_ui_vbox_child input.cke_dialog_ui_input_text{height:24px}.cke_skin_silva div.cke_dialog_ui_input_text,.cke_skin_silva div.cke_dialog_ui_input_password,.cke_skin_silvadiv.cke_dialog_ui_input_textarea{padding:0!important}.portlets p span.orientation{display:block;margin-top:.3em;border:1px dotted black;text-align:center}.portlets p span.portrait{width:4.5em;height:6em}.portlets p span.landscape{width:6.5em;height:3.5em}.portlets p span.square{width:4em;height:4em}.portlets .references .item{clear:both;margin:0 5px}.portlets .references .item a.edit-screen{float:right;margin-right:5px;cu
 rsor:pointer}.portlets .references .item p{margin-left:20px}.portlets .references .item img{float:left;margin:5px;cursor:inherit}
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/css/smi.css
--- a/src/silva/ui/static/css/smi.css	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/css/smi.css	Thu Sep 26 14:33:42 2013 +0200
@@ -231,13 +231,13 @@
 }
 
 #workspace .header .user-menu li ins.tab-logo {
-  background-image: url('../img/member_icon_transp_bg_bw.png'); 
-  margin: 4px; 
+  background-image: url('../img/member_icon_transp_bg_bw.png');
+  margin: 4px;
   margin-right: 0;
 }
 
 #workspace .header li div.tab-icon {
-  float: left;     
+  float: left;
 }
 
 #workspace .header .tabs li.sub-level a.active {
@@ -396,6 +396,12 @@
   background-position: -44px -142px;
 }
 
+ins.form_warning {
+  width: 12px;
+  height: 16px;
+  background-position: -84px -142px;
+}
+
 ins.ui-icon-arrowreturnthick-1-w {
   width: 12px;
   height: 16px;
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/css/smi.min.css
--- a/src/silva/ui/static/css/smi.min.css	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/css/smi.min.css	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-html{background:#7996ac;overflow:hidden}a{text-decoration:none;cursor:pointer;color:inherit}a ins{cursor:pointer}a span{cursor:pointer}input,textarea{cursor:text}#loading-message{display:none;padding:2px 0;position:fixed;right:50%;top:0;z-index:200}#content-url{display:none}#smi-text{position:absolute;top:20%;left:15%;width:70%;height:250px;-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;background:#fff;bottom:0;cursor:default;z-index:800}#smi-text .smi-text-content{padding:1em}div.jGrowl{z-index:13000}div.jGrowl div.error{background-color:#c00}div.batching a,div.batching span{display:block;float:left;margin:0 .25em}div.batching a.previous ins{float:left;margin-right:.2em}div.batching a.next ins{float:right;margin-left:.2em}div.batchi
 ng a.current{font-weight:700}#main{position:absolute;top:20px;bottom:20px;left:20px;right:20px}#smi-cover{position:absolute;width:100%;height:100%;z-index:600}#smi-handle{position:absolute;t
 op:46%;bottom:0;left:288px;width:4px;height:40px;background-color:#666;z-index:700;cursor:move;-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}#workspace{-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;background:#fff;bottom:0;left:300px;position:absolute;right:0;top:0;cursor:default}.focus.highlight{outline:2px #fb0 solid}.message ins.ui-icon{float:left}#workspace .header{background:url(../img/gradient50.png) repeat-x bottom;border-bottom:1px #999 solid;color:#111;height:66px;line-height:24px;padding:0 10px 33px 10px}#workspace .header a{color:#111}#workspace .header h2{clear:both;color:#666;font-size:1.3em;paddin
 g:8px 0 4px 0;height:24px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#workspace .header h2 ins.icon{background-color:transparent;background-image:url(../img/sprite.png);backgr
 ound-repeat:no-repeat;display:block;float:left;height:20px;margin-right:4px;width:20px}#workspace .header .toolbar .view-toolbar{float:right;margin:4px}#workspace .compact-header{height:0}#workspace .compact-header h2{float:left;clear:right;padding:6px;max-width:80%}#workspace .compact-header .toolbar{position:absolute;right:120px;background:url(../img/gradient50.png) repeat-x bottom}#workspace .compact-header .toolbar .selector{float:right}#workspace .header .tabs{float:left;list-style:none;margin:0;padding:0}#workspace .header .subtabs{-webkit-border-radius:3px 3px 3px 3px;-moz-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;-webkit-box-shadow:#A0A0A0 0 2px 8px;-moz-box-shadow:#A0A0A0 0 2px 8px;box-shadow:#A0A0A0 0 2px 8px;position:absolute;width:210px;top:32px;background:#f
 ff;border:1px #AAA solid;margin:0;list-style:none;display:none;z-index:1200}#workspace .header .tabs li{position:relative}#workspace .header .tabs li a{display:block;padding:0 5px}#workspace
  .header .compact-tabs li ins.tab-logo,#workspace .header .user-menu li ins.tab-logo{background-color:transparent;background-image:url(../img/menu_lines_icon.png);background-repeat:no-repeat;display:block;float:left;height:18px;width:24px;margin:5px 0 3px 4px}#workspace .header .user-menu li ins.tab-logo{background-image:url(../img/member_icon_transp_bg_bw.png);margin:4px;margin-right:0}#workspace .header li div.tab-icon{float:left}#workspace .header .tabs li.sub-level a.active{color:#fff;background-color:#4d96e6}#workspace .header .tabs li.sub-level a:hover{background:url(../img/gradient-tab-selected.png) repeat-x;color:#fff;text-shadow:#373737 1px 1px 1px}#workspace .header .tabs li.top-level{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-web
 kit-box-shadow:inset 0 0 2px #aaa;-moz-box-shadow:inset 0 0 2px #aaa;box-shadow:inset 0 0 2px #aaa;background:url(../img/gradient-tab.png) repeat-x;color:#222;display:block;float:left;margin
 -right:10px;border:1px solid #777;border-top:0}#workspace .header .tabs a.top-entry{height:26px;line-height:26px;padding:0}#workspace .header .tabs a .subtab-icon{float:right}#workspace .header .tabs a.top-entry .subtab-icon ins{background:url(../img/sprite.png) no-repeat;background-position:-117px -96px;display:block;height:26px}#workspace .header .tabs a.top-label .subtab-icon ins{background-position:-124px -96px;width:18px}#workspace .header .tabs a.top-label.active .subtab-icon ins{background-position:-124px -116px}#workspace .header .tabs a.top-screen .subtab-icon{border-left:1px solid #555}#workspace .header .tabs a.top-screen .subtab-icon ins{height:26px;width:26px}#workspace .header .tabs a.top-entry span{float:left;display:block;height:26px;padding:0 10px 0 10px}#workspace .heade
 r .tabs a.top-entry.active{color:#fff;text-shadow:#373737 1px 1px 1px;font-weight:700}#workspace .header .tabs a.top-screen.active span,#workspace .header .tabs a.top-label.active{background
 :url(../img/gradient-tab-selected.png) repeat-x}#workspace .header .actions{margin:5px 5px 5px 0}#workspace .header .compact-tabs,#workspace .header .user-menu{display:none;position:absolute;top:0;right:56px}#workspace .header .user-menu{right:0}#workspace .header .view-actions{position:absolute;top:0;right:61px;display:none}#workspace .header .admin-actions{margin:5px 10px 0 0}#workspace .header .admin-actions li a{padding:2px 2px}#workspace .content{position:absolute;top:100px;bottom:0;left:0;right:0;overflow:hidden;overflow-y:auto}#workspace .extended-content{top:34px}#workspace .iframe-view{overflow-x:inherit;overflow-y:inherit;text-align:center}@media screen and (orientation: portrait){#navigation{ display:none!important}#smi-handle{display:none!important}#workspace{left:0!important}
 }@media screen and (max-width: 900px){#navigation{ display:none!important}#smi-handle{display:none!important}#workspace{left:0!important}}ins.icon{width:20px;height:20px;display:block;float:
 left;background-image:url(../img/sprite.png);margin-right:5px}ins.loading{background:#fff url(../img/loading.gif) no-repeat center center!important}ins.form_check{width:12px;height:16px;background-position:-44px -142px}ins.ui-icon-arrowreturnthick-1-w{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-position:-124px -141px;margin-right:5px}ins.ui-icon-check{background-image:url(../img/sprite.png)!important;background-position:-44px -142px}ins.ui-icon-radio-on{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-position:-44px -142px;margin-right:5px}ins.ui-icon-close{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-position:-64px -142px;margin-right:5px}.user-menu ins.ui-icon,.compact-tabs 
 ins.ui-icon{background-image:url(../jqueryui/images/ui-icons_222222_256x240.png)!important;width:12px;height:12px;margin-right:5px;margin-top:5px}.user-menu ins.ui-icon-preferences{backgroun
 d-position:-178px -113px}.user-menu ins.ui-icon-about{background-position:-18px -146px}.user-menu ins.ui-icon-manage{background-position:-226px -98px}.user-menu ins.ui-icon-services{background-position:-114px -98px}.user-menu ins.ui-icon-logout{background-position:-3px -177px}ins.silva_autotoc{background-position:-100px -41px!important}ins.silva_csvsource{background-position:-120px -41px!important}ins.silva_document{background-position:0 -21px!important}ins.silva_page{background-position:-20px -21px!important}ins.silva_file{background-position:-100px -21px!important}ins.silva_find{background-position:-120px -21px!important}ins.silva_folder{background-position:-40px -21px!important}ins.silva_ghostfolder{background-position:-20px -41px!important}ins.silva_ghost{background-position:0 -41px!i
 mportant}ins.silva_image{background-position:-80px -21px!important}ins.silva_indexer{background-position:-40px -41px!important}ins.silva_link{background-position:-60px -41px!important}ins.si
 lva_permanentredirectlink{background-position:-80px -41px!important}ins.silva_publication{background-position:-60px -21px!important}ins.silva_root{background-position:-160px -121px!important}ins.silva_sourceasset{background-position:-140px -41px!important}ins.silva_agendaitem{background-position:-20px -61px!important}ins.silva_agendapage{background-position:-40px -61px!important}ins.silva_agendafilter{background-position:-120px -61px!important}ins.silva_agendaviewer{background-position:-100px -61px!important}ins.silva_newsitem{background-position:0 -81px!important}ins.silva_newspage{background-position:0 -61px!important}ins.silva_newsfilter{background-position:-80px -61px!important}ins.silva_newscategoryfilter{background-position:-160px -61px!important}ins.silva_newsviewer{background-posi
 tion:-60px -61px!important}ins.silva_newspublication{background-position:-160px -41px!important}ins.silva_rssaggregator{background-position:-140px -61px!important}ins.state{background-image:
 url(../img/sprite.png);display:block;float:left;width:10px!important;height:10px!important}ins.state.draft{background-position:-5px -105px}ins.state.unapproved{background-position:-5px -105px}ins.state.pending{background-position:-25px -105px}ins.state.approved{background-position:-45px -105px}ins.state.published{background-position:-65px -105px}ins.state.closed{background-position:-85px -105px}ins.state.last_closed{background-position:-85px -105px}#form div.versioning-state-cell ins.state{margin:3px 3px 0 0}#form div.versioning-state-cell{display:block;padding-left:3px}#form div.versioning-state-cell a{text-decoration:underline}body .ui-state-default,body .ui-state-active{background:inherit;color:inherit;border:inherit}body .ui-widget .ui-button-text{line-height:inherit}body .ui-widget{f
 ont-family:sans-serif;font-size:inherit}body .upload-file .upload-progress{width:200px;height:10px}body .ui-dialog{-webkit-box-shadow:#A0A0A0 0 2px 8px;-moz-box-shadow:#A0A0A0 0 2px 8px;box-
 shadow:#A0A0A0 0 2px 8px}.icon.silva_dotted_horizontal{background-image:url(../img/sprite.png);background-position:-158px -87px!important;background-repeat:no-repeat}
\ No newline at end of file
+html{background:#7996ac;overflow:hidden}a{text-decoration:none;cursor:pointer;color:inherit}a ins{cursor:pointer}a span{cursor:pointer}input,textarea{cursor:text}#loading-message{display:none;padding:2px 0;position:fixed;right:50%;top:0;z-index:200}#content-url{display:none}#smi-text{position:absolute;top:20%;left:15%;width:70%;height:250px;-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;background:#fff;bottom:0;cursor:default;z-index:800}#smi-text .smi-text-content{padding:1em}div.jGrowl{z-index:13000}div.jGrowl div.error{background-color:#c00}div.batching a,div.batching span{display:block;float:left;margin:0 .25em}div.batching a.previous ins{float:left;margin-right:.2em}div.batching a.next ins{float:right;margin-left:.2em}div.batchi
 ng a.current{font-weight:700}#main{position:absolute;top:20px;bottom:20px;left:20px;right:20px}#smi-cover{position:absolute;width:100%;height:100%;z-index:600}#smi-handle{position:absolute;t
 op:46%;bottom:0;left:288px;width:4px;height:40px;background-color:#666;z-index:700;cursor:move;-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}#workspace{-webkit-box-shadow:#1c2e3d 0 3px 10px;-moz-box-shadow:#1c2e3d 0 3px 10px;box-shadow:#1c2e3d 0 3px 10px;background:#fff;bottom:0;left:300px;position:absolute;right:0;top:0;cursor:default}.focus.highlight{outline:2px #fb0 solid}.message ins.ui-icon{float:left}#workspace .header{background:url(../img/gradient50.png) repeat-x bottom;border-bottom:1px #999 solid;color:#111;height:66px;line-height:24px;padding:0 10px 33px 10px}#workspace .header a{color:#111}#workspace .header h2{clear:both;color:#666;font-size:1.3em;paddin
 g:8px 0 4px 0;height:24px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#workspace .header h2 ins.icon{background-color:transparent;background-image:url(../img/sprite.png);backgr
 ound-repeat:no-repeat;display:block;float:left;height:20px;margin-right:4px;width:20px}#workspace .header .toolbar .view-toolbar{float:right;margin:4px}#workspace .compact-header{height:0}#workspace .compact-header h2{float:left;clear:right;padding:6px;max-width:80%}#workspace .compact-header .toolbar{position:absolute;right:120px;background:url(../img/gradient50.png) repeat-x bottom}#workspace .compact-header .toolbar .selector{float:right}#workspace .header .tabs{float:left;list-style:none;margin:0;padding:0}#workspace .header .subtabs{-webkit-border-radius:3px 3px 3px 3px;-moz-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;-webkit-box-shadow:#A0A0A0 0 2px 8px;-moz-box-shadow:#A0A0A0 0 2px 8px;box-shadow:#A0A0A0 0 2px 8px;position:absolute;width:210px;top:32px;background:#f
 ff;border:1px #AAA solid;margin:0;list-style:none;display:none;z-index:1200}#workspace .header .tabs li{position:relative}#workspace .header .tabs li a{display:block;padding:0 5px}#workspace
  .header .compact-tabs li ins.tab-logo,#workspace .header .user-menu li ins.tab-logo{background-color:transparent;background-image:url(../img/menu_lines_icon.png);background-repeat:no-repeat;display:block;float:left;height:18px;width:24px;margin:5px 0 3px 4px}#workspace .header .user-menu li ins.tab-logo{background-image:url(../img/member_icon_transp_bg_bw.png);margin:4px;margin-right:0}#workspace .header li div.tab-icon{float:left}#workspace .header .tabs li.sub-level a.active{color:#fff;background-color:#4d96e6}#workspace .header .tabs li.sub-level a:hover{background:url(../img/gradient-tab-selected.png) repeat-x;color:#fff;text-shadow:#373737 1px 1px 1px}#workspace .header .tabs li.top-level{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-web
 kit-box-shadow:inset 0 0 2px #aaa;-moz-box-shadow:inset 0 0 2px #aaa;box-shadow:inset 0 0 2px #aaa;background:url(../img/gradient-tab.png) repeat-x;color:#222;display:block;float:left;margin
 -right:10px;border:1px solid #777;border-top:0}#workspace .header .tabs a.top-entry{height:26px;line-height:26px;padding:0}#workspace .header .tabs a .subtab-icon{float:right}#workspace .header .tabs a.top-entry .subtab-icon ins{background:url(../img/sprite.png) no-repeat;background-position:-117px -96px;display:block;height:26px}#workspace .header .tabs a.top-label .subtab-icon ins{background-position:-124px -96px;width:18px}#workspace .header .tabs a.top-label.active .subtab-icon ins{background-position:-124px -116px}#workspace .header .tabs a.top-screen .subtab-icon{border-left:1px solid #555}#workspace .header .tabs a.top-screen .subtab-icon ins{height:26px;width:26px}#workspace .header .tabs a.top-entry span{float:left;display:block;height:26px;padding:0 10px 0 10px}#workspace .heade
 r .tabs a.top-entry.active{color:#fff;text-shadow:#373737 1px 1px 1px;font-weight:700}#workspace .header .tabs a.top-screen.active span,#workspace .header .tabs a.top-label.active{background
 :url(../img/gradient-tab-selected.png) repeat-x}#workspace .header .actions{margin:5px 5px 5px 0}#workspace .header .compact-tabs,#workspace .header .user-menu{display:none;position:absolute;top:0;right:56px}#workspace .header .user-menu{right:0}#workspace .header .view-actions{position:absolute;top:0;right:61px;display:none}#workspace .header .admin-actions{margin:5px 10px 0 0}#workspace .header .admin-actions li a{padding:2px 2px}#workspace .content{position:absolute;top:100px;bottom:0;left:0;right:0;overflow:hidden;overflow-y:auto}#workspace .extended-content{top:34px}#workspace .iframe-view{overflow-x:inherit;overflow-y:inherit;text-align:center}@media screen and (orientation: portrait){#navigation{ display:none!important}#smi-handle{display:none!important}#workspace{left:0!important}
 }@media screen and (max-width: 900px){#navigation{ display:none!important}#smi-handle{display:none!important}#workspace{left:0!important}}ins.icon{width:20px;height:20px;display:block;float:
 left;background-image:url(../img/sprite.png);margin-right:5px}ins.loading{background:#fff url(../img/loading.gif) no-repeat center center!important}ins.form_check{width:12px;height:16px;background-position:-44px -142px}ins.form_warning{width:12px;height:16px;background-position:-84px -142px}ins.ui-icon-arrowreturnthick-1-w{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-position:-124px -141px;margin-right:5px}ins.ui-icon-check{background-image:url(../img/sprite.png)!important;background-position:-44px -142px}ins.ui-icon-radio-on{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-position:-44px -142px;margin-right:5px}ins.ui-icon-close{width:12px;height:16px;background-image:url(../img/sprite.png)!important;background-pos
 ition:-64px -142px;margin-right:5px}.user-menu ins.ui-icon,.compact-tabs ins.ui-icon{background-image:url(../jqueryui/images/ui-icons_222222_256x240.png)!important;width:12px;height:12px;mar
 gin-right:5px;margin-top:5px}.user-menu ins.ui-icon-preferences{background-position:-178px -113px}.user-menu ins.ui-icon-about{background-position:-18px -146px}.user-menu ins.ui-icon-manage{background-position:-226px -98px}.user-menu ins.ui-icon-services{background-position:-114px -98px}.user-menu ins.ui-icon-logout{background-position:-3px -177px}ins.silva_autotoc{background-position:-100px -41px!important}ins.silva_csvsource{background-position:-120px -41px!important}ins.silva_document{background-position:0 -21px!important}ins.silva_page{background-position:-20px -21px!important}ins.silva_file{background-position:-100px -21px!important}ins.silva_find{background-position:-120px -21px!important}ins.silva_folder{background-position:-40px -21px!important}ins.silva_ghostfolder{background-pos
 ition:-20px -41px!important}ins.silva_ghost{background-position:0 -41px!important}ins.silva_image{background-position:-80px -21px!important}ins.silva_indexer{background-position:-40px -41px!
 important}ins.silva_link{background-position:-60px -41px!important}ins.silva_permanentredirectlink{background-position:-80px -41px!important}ins.silva_publication{background-position:-60px -21px!important}ins.silva_root{background-position:-160px -121px!important}ins.silva_sourceasset{background-position:-140px -41px!important}ins.silva_agendaitem{background-position:-20px -61px!important}ins.silva_agendapage{background-position:-40px -61px!important}ins.silva_agendafilter{background-position:-120px -61px!important}ins.silva_agendaviewer{background-position:-100px -61px!important}ins.silva_newsitem{background-position:0 -81px!important}ins.silva_newspage{background-position:0 -61px!important}ins.silva_newsfilter{background-position:-80px -61px!important}ins.silva_newscategoryfilter{backgr
 ound-position:-160px -61px!important}ins.silva_newsviewer{background-position:-60px -61px!important}ins.silva_newspublication{background-position:-160px -41px!important}ins.silva_rssaggregat
 or{background-position:-140px -61px!important}ins.state{background-image:url(../img/sprite.png);display:block;float:left;width:10px!important;height:10px!important}ins.state.draft{background-position:-5px -105px}ins.state.unapproved{background-position:-5px -105px}ins.state.pending{background-position:-25px -105px}ins.state.approved{background-position:-45px -105px}ins.state.published{background-position:-65px -105px}ins.state.closed{background-position:-85px -105px}ins.state.last_closed{background-position:-85px -105px}#form div.versioning-state-cell ins.state{margin:3px 3px 0 0}#form div.versioning-state-cell{display:block;padding-left:3px}#form div.versioning-state-cell a{text-decoration:underline}body .ui-state-default,body .ui-state-active{background:inherit;color:inherit;border:inhe
 rit}body .ui-widget .ui-button-text{line-height:inherit}body .ui-widget{font-family:sans-serif;font-size:inherit}body .upload-file .upload-progress{width:200px;height:10px}body .ui-dialog{-w
 ebkit-box-shadow:#A0A0A0 0 2px 8px;-moz-box-shadow:#A0A0A0 0 2px 8px;box-shadow:#A0A0A0 0 2px 8px}.icon.silva_dotted_horizontal{background-image:url(../img/sprite.png);background-position:-158px -87px!important;background-repeat:no-repeat}
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/img/minus-small.png
Binary file src/silva/ui/static/img/minus-small.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/img/plus-small.png
Binary file src/silva/ui/static/img/plus-small.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/img/settings.png
Binary file src/silva/ui/static/img/settings.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/img/sprite-local.png
Binary file src/silva/ui/static/img/sprite-local.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/img/sprite.png
Binary file src/silva/ui/static/img/sprite.png has changed
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.js
--- a/src/silva/ui/static/js/infrae.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.js	Thu Sep 26 14:33:42 2013 +0200
@@ -119,6 +119,17 @@
             };
             return result;
         },
+        map_concat: function(array, callback, result) {
+            var index, len;
+
+            if (result === undefined) {
+                result = [];
+            };
+            for (index=0, len=array.length; index < len; index++) {
+                result = result.concat(callback(array[index]));
+            };
+            return result;
+        },
         lazy_map: function(array, callback, result) {
             var index = 0;
             var len = array.length;
@@ -159,4 +170,3 @@
 
     infrae.utils = module;
 })(infrae, jQuery);
-
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.min.js
--- a/src/silva/ui/static/js/infrae.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function(window){var infrae={};window.infrae=infrae})(window),function(infrae,$){var module={},predicates={};$.extend(predicates,{or:function(){for(var max=arguments.length,index=1;max>index;){if(predicates[arguments[index][0]].apply(this,arguments[index])===!0)return!0;index+=1}return!1},and:function(){for(var max=arguments.length,index=1;max>index;){if(predicates[arguments[index][0]].apply(this,arguments[index])!==!0)return!1;index+=1}return!0},not:function(){return!predicates[arguments[1][0]].apply(this,arguments[1])},equal:function(){for(var value=this[arguments[1]],max=arguments.length,index=2;max>index;){if(arguments[index]==value)return!0;index+=1}return!1},provides:function(){for(var max=arguments.length,index=1;max>index;){if(infrae.interfaces.is_implemented_by(arguments[index],
 this))return!0;index+=1}return!1}}),$.extend(module,{test:function(data,conditions){return predicates[conditions[0]].apply(data,conditions)},each:function(array,callback){var index,len;for(i
 ndex=0,len=array.length;len>index;index++)callback(array[index])},apply_on_each:function(array,callback){var index,len;for(index=0,len=array.length;len>index;index++)callback.apply(array[index])},map:function(array,callback,result){var index,len;for(void 0===result&&(result=[]),index=0,len=array.length;len>index;index++)result.push(callback(array[index]));return result},lazy_map:function(array,callback,result){var index=0,len=array.length,deferred=$.Deferred();void 0===result&&(result=[]);var work=function(){for(var start=+new Date;len>index;index++)if(result.push(callback(array[index])),+new Date-start>50)return setTimeout(work,50),void 0;deferred.resolve(result)};return work(),deferred.promise()},apply_on_map:function(array,callback,result){var index,len;for(void 0===result&&(result=[])
 ,index=0,len=array.length;len>index;index++)result.push(callback.apply(array[index]));return result}}),infrae.utils=module}(infrae,jQuery);
\ No newline at end of file
+(function(window){var infrae={};window.infrae=infrae})(window),function(infrae,$){var module={},predicates={};$.extend(predicates,{or:function(){for(var max=arguments.length,index=1;max>index;){if(predicates[arguments[index][0]].apply(this,arguments[index])===!0)return!0;index+=1}return!1},and:function(){for(var max=arguments.length,index=1;max>index;){if(predicates[arguments[index][0]].apply(this,arguments[index])!==!0)return!1;index+=1}return!0},not:function(){return!predicates[arguments[1][0]].apply(this,arguments[1])},equal:function(){for(var value=this[arguments[1]],max=arguments.length,index=2;max>index;){if(arguments[index]==value)return!0;index+=1}return!1},provides:function(){for(var max=arguments.length,index=1;max>index;){if(infrae.interfaces.is_implemented_by(arguments[index],
 this))return!0;index+=1}return!1}}),$.extend(module,{test:function(data,conditions){return predicates[conditions[0]].apply(data,conditions)},each:function(array,callback){var index,len;for(i
 ndex=0,len=array.length;len>index;index++)callback(array[index])},apply_on_each:function(array,callback){var index,len;for(index=0,len=array.length;len>index;index++)callback.apply(array[index])},map:function(array,callback,result){var index,len;for(void 0===result&&(result=[]),index=0,len=array.length;len>index;index++)result.push(callback(array[index]));return result},map_concat:function(array,callback,result){var index,len;for(void 0===result&&(result=[]),index=0,len=array.length;len>index;index++)result=result.concat(callback(array[index]));return result},lazy_map:function(array,callback,result){var index=0,len=array.length,deferred=$.Deferred();void 0===result&&(result=[]);var work=function(){for(var start=+new Date;len>index;index++)if(result.push(callback(array[index])),+new Date-s
 tart>50)return setTimeout(work,50),void 0;deferred.resolve(result)};return work(),deferred.promise()},apply_on_map:function(array,callback,result){var index,len;for(void 0===result&&(result=
 []),index=0,len=array.length;len>index;index++)result.push(callback.apply(array[index]));return result}}),infrae.utils=module}(infrae,jQuery);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.ui.js
--- a/src/silva/ui/static/js/infrae.ui.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.ui.js	Thu Sep 26 14:33:42 2013 +0200
@@ -172,7 +172,7 @@
         ConfirmationDialog: function(data) {
             var deferred = $.Deferred();
             var $message = $('<div></div>');
-            var configuration = {autoOpen: false, modal: true, buttons: {}};
+            var configuration = {autoOpen: false, modal: true, buttons: {}, zIndex: 12000};
 
             if (data.title) {
                 $message.attr('title', data.title);
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.ui.min.js
--- a/src/silva/ui/static/js/infrae.ui.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.ui.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function(infrae,$){var module={},dialog_options={maxFactor:.8,minFactor:0,threshold:15,maxWidth:0,maxHeight:0,minWidth:0,maxHeight:0,position:["center","center"]};$.extend(module,{icon:function($content,icon){0>icon.indexOf(".")?$content.addClass(icon):$content.attr("style","background:url("+icon+") no-repeat center center;")},selection:{disable:function($content){return $content.disableSelection()},enable:function($content){return $content.enableSelection()}},ShowDialog:function($dialog,options){var $window=$(window),$widget=$dialog.dialog("widget");if(void 0!==options)for(var name in dialog_options)void 0===options[name]&&(options[name]=dialog_options[name]);else options=dialog_options;var resize=function(initial){var window_width=$window.width(),window_height=$window.height(),widget_w
 idth=$widget.width(),widget_height=$widget.height(),min_width=Math.ceil(window_width*options.minFactor),min_height=Math.ceil(window_height*options.minFactor),max_width=Math.ceil(window_width
 *options.maxFactor),max_height=Math.ceil(window_height*options.maxFactor),popup_position=$widget.position(),need_reposition=initial===!0,changed_size=!1;options.maxWidth&&(max_width=Math.min(options.maxWidth,max_width)),options.maxHeight&&(max_height=Math.min(options.maxHeight,max_height)),options.minWidth&&(min_width=Math.max(options.minWidth,min_width)),options.minHeight&&(min_height=Math.max(options.minHeight,min_height)),min_height>widget_height?($dialog.dialog("option","height",min_height),widget_height=min_height,changed_size=!0,min_height>max_height&&(max_height=min_height)):widget_height>max_height&&($dialog.dialog("option","height",max_height),widget_height=max_height,changed_size=!0),popup_position.top&&0>window_height-(popup_position.top+widget_height+options.threshold)&&(need_
 reposition=!0),min_width>widget_width?($dialog.dialog("option","width",min_width),widget_width=min_width,changed_size=!0,min_width>max_width&&(max_width=min_width)):widget_width>max_width&&(
 $dialog.dialog("option","width",max_width),widget_width=max_width,changed_size=!0),popup_position.left&&0>window_width-(popup_position.left+widget_width+options.threshold)&&(need_reposition=!0),$dialog.dialog("option","maxHeight",max_height),$dialog.dialog("option","maxWidth",max_width),$dialog.dialog("option","minHeight",min_height),$dialog.dialog("option","minWidth",min_width),need_reposition&&$dialog.dialog("option","position",options.position),changed_size&&$dialog.trigger("infrae-ui-dialog-resized",{height:widget_height,width:widget_width})},handler=function(event){event.target===window?setTimeout(resize,0):event.target===$widget.get(0)&&$dialog.trigger("infrae-ui-dialog-resized",{height:$widget.height(),width:$widget.width()})};$window.bind("resize.infrae-ui-dialog",handler),$dialog
 .one("dialogclose",function(){$window.unbind("resize.infrae-ui-dialog",handler),$dialog.unbind("infrae-ui-dialog-resize")}),$dialog.bind("infrae-ui-dialog-resize",function(){resize()}),$dial
 og.dialog("open"),resize(!0)},ResizeDialog:function($dialog){$dialog.dialog("isOpen")&&$dialog.trigger("infrae-ui-dialog-resize")},ConfirmationDialog:function(data){var deferred=$.Deferred(),$message=$("<div></div>"),configuration={autoOpen:!1,modal:!0,buttons:{}};return data.title&&$message.attr("title",data.title),$message.html(data.message),data.buttons||(data.buttons={Cancel:function(){return!1},Continue:function(){return!0}},data.not_cancellable&&delete data.buttons.Cancel),$.each(data.buttons,function(name,callback){configuration.buttons[name]=function(){var result=callback();void 0!==result.then?result.then(deferred.resolve,deferred.reject):result?deferred.resolve():deferred.reject()}}),$message.bind("dialogclose",function(){$message.remove(),deferred.reject()}),deferred.always(fun
 ction(){$message.dialog("close")}),$message.dialog(configuration),infrae.ui.ShowDialog($message),deferred.promise()},scroll:function($content,speed,direction,distance,interval){var target,po
 sition=$content.scrollTop();switch(direction){case"up":target=position-distance,interval&&(target-=target%interval);break;case"down":target=position+distance,interval&&0!=position%interval&&(target+=interval-target%interval);break;case"absolute":default:target=distance}return $content.stop(!0,!0),$content.animate({scrollTop:target},speed),$content.promise()},update_table_columns:function($table,options,$reference){return"object"!=typeof options.fixed&&(options.fixed={}),$table.each(function(){var $table=$(this),colComputedWidths={},fixedWidths={},totalWidth=0;if(void 0!==$reference){var regex=/([0-9]+)([em%]*)/;$reference.find("colgroup col").each(function(i){var match=$(this).attr("width").match(regex);match&&(colComputedWidths[i]=match[1],fixedWidths[i]=match[2])})}else $table.css("tabl
 e-layout","auto"),$table.find("tr:visible:first td").each(function(i){options.fixed[i]?(colComputedWidths[i]=options.fixed[i],fixedWidths[i]=null):colComputedWidths[i]=$(this).outerWidth(),t
 otalWidth+=colComputedWidths[i]});$table.css("table-layout","fixed"),$table.find("colgroup col").each(function(i){var newColWidth;void 0!==fixedWidths[i]?(newColWidth=colComputedWidths[i],fixedWidths[i]&&(newColWidth+=fixedWidths[i])):newColWidth=Math.round(100*(colComputedWidths[i]/totalWidth))+"%",$(this).attr("width",newColWidth)})})}}),infrae.ui=module})(infrae,jQuery);
\ No newline at end of file
+(function(infrae,$){var module={},dialog_options={maxFactor:.8,minFactor:0,threshold:15,maxWidth:0,maxHeight:0,minWidth:0,maxHeight:0,position:["center","center"]};$.extend(module,{icon:function($content,icon){0>icon.indexOf(".")?$content.addClass(icon):$content.attr("style","background:url("+icon+") no-repeat center center;")},selection:{disable:function($content){return $content.disableSelection()},enable:function($content){return $content.enableSelection()}},ShowDialog:function($dialog,options){var $window=$(window),$widget=$dialog.dialog("widget");if(void 0!==options)for(var name in dialog_options)void 0===options[name]&&(options[name]=dialog_options[name]);else options=dialog_options;var resize=function(initial){var window_width=$window.width(),window_height=$window.height(),widget_w
 idth=$widget.width(),widget_height=$widget.height(),min_width=Math.ceil(window_width*options.minFactor),min_height=Math.ceil(window_height*options.minFactor),max_width=Math.ceil(window_width
 *options.maxFactor),max_height=Math.ceil(window_height*options.maxFactor),popup_position=$widget.position(),need_reposition=initial===!0,changed_size=!1;options.maxWidth&&(max_width=Math.min(options.maxWidth,max_width)),options.maxHeight&&(max_height=Math.min(options.maxHeight,max_height)),options.minWidth&&(min_width=Math.max(options.minWidth,min_width)),options.minHeight&&(min_height=Math.max(options.minHeight,min_height)),min_height>widget_height?($dialog.dialog("option","height",min_height),widget_height=min_height,changed_size=!0,min_height>max_height&&(max_height=min_height)):widget_height>max_height&&($dialog.dialog("option","height",max_height),widget_height=max_height,changed_size=!0),popup_position.top&&0>window_height-(popup_position.top+widget_height+options.threshold)&&(need_
 reposition=!0),min_width>widget_width?($dialog.dialog("option","width",min_width),widget_width=min_width,changed_size=!0,min_width>max_width&&(max_width=min_width)):widget_width>max_width&&(
 $dialog.dialog("option","width",max_width),widget_width=max_width,changed_size=!0),popup_position.left&&0>window_width-(popup_position.left+widget_width+options.threshold)&&(need_reposition=!0),$dialog.dialog("option","maxHeight",max_height),$dialog.dialog("option","maxWidth",max_width),$dialog.dialog("option","minHeight",min_height),$dialog.dialog("option","minWidth",min_width),need_reposition&&$dialog.dialog("option","position",options.position),changed_size&&$dialog.trigger("infrae-ui-dialog-resized",{height:widget_height,width:widget_width})},handler=function(event){event.target===window?setTimeout(resize,0):event.target===$widget.get(0)&&$dialog.trigger("infrae-ui-dialog-resized",{height:$widget.height(),width:$widget.width()})};$window.bind("resize.infrae-ui-dialog",handler),$dialog
 .one("dialogclose",function(){$window.unbind("resize.infrae-ui-dialog",handler),$dialog.unbind("infrae-ui-dialog-resize")}),$dialog.bind("infrae-ui-dialog-resize",function(){resize()}),$dial
 og.dialog("open"),resize(!0)},ResizeDialog:function($dialog){$dialog.dialog("isOpen")&&$dialog.trigger("infrae-ui-dialog-resize")},ConfirmationDialog:function(data){var deferred=$.Deferred(),$message=$("<div></div>"),configuration={autoOpen:!1,modal:!0,buttons:{},zIndex:12e3};return data.title&&$message.attr("title",data.title),$message.html(data.message),data.buttons||(data.buttons={Cancel:function(){return!1},Continue:function(){return!0}},data.not_cancellable&&delete data.buttons.Cancel),$.each(data.buttons,function(name,callback){configuration.buttons[name]=function(){var result=callback();void 0!==result.then?result.then(deferred.resolve,deferred.reject):result?deferred.resolve():deferred.reject()}}),$message.bind("dialogclose",function(){$message.remove(),deferred.reject()}),deferre
 d.always(function(){$message.dialog("close")}),$message.dialog(configuration),infrae.ui.ShowDialog($message),deferred.promise()},scroll:function($content,speed,direction,distance,interval){v
 ar target,position=$content.scrollTop();switch(direction){case"up":target=position-distance,interval&&(target-=target%interval);break;case"down":target=position+distance,interval&&0!=position%interval&&(target+=interval-target%interval);break;case"absolute":default:target=distance}return $content.stop(!0,!0),$content.animate({scrollTop:target},speed),$content.promise()},update_table_columns:function($table,options,$reference){return"object"!=typeof options.fixed&&(options.fixed={}),$table.each(function(){var $table=$(this),colComputedWidths={},fixedWidths={},totalWidth=0;if(void 0!==$reference){var regex=/([0-9]+)([em%]*)/;$reference.find("colgroup col").each(function(i){var match=$(this).attr("width").match(regex);match&&(colComputedWidths[i]=match[1],fixedWidths[i]=match[2])})}else $tab
 le.css("table-layout","auto"),$table.find("tr:visible:first td").each(function(i){options.fixed[i]?(colComputedWidths[i]=options.fixed[i],fixedWidths[i]=null):colComputedWidths[i]=$(this).ou
 terWidth(),totalWidth+=colComputedWidths[i]});$table.css("table-layout","fixed"),$table.find("colgroup col").each(function(i){var newColWidth;void 0!==fixedWidths[i]?(newColWidth=colComputedWidths[i],fixedWidths[i]&&(newColWidth+=fixedWidths[i])):newColWidth=Math.round(100*(colComputedWidths[i]/totalWidth))+"%",$(this).attr("width",newColWidth)})})}}),infrae.ui=module})(infrae,jQuery);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.views.js
--- a/src/silva/ui/static/js/infrae.views.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.views.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,87 +1,151 @@
 
 (function ($, infrae, jsontemplate) {
+    var BUNDLE_RE = /^:bundle:/;
 
     // Disable all ajax caching, for IE 8.
     $.ajaxSetup({
         cache:false
     });
 
+    var HTMLResourceTracker = function() {
+        var urls = [];
+
+        var analyze_url = function(original) {
+            var parts = original.split('/'),
+                last = parts[parts.length - 1];
+
+            if (last.match(BUNDLE_RE)) {
+                parts.pop();
+
+                var files = last.substr(8).split(';'),
+                    prefix = parts.join('/'),
+                    result = [],
+                    i;
+
+                for (i=0; i < files.length; i++) {
+                    result.push({url: [prefix, files[i]].join('/'),
+                                 file: files[i],
+                                 prefix: prefix});
+                };
+                return result;
+            };
+            return [{url: original, file: null, prefix: original}];
+        };
+
+        return {
+            register: function(new_url) {
+                var new_urls = analyze_url(new_url),
+                    i;
+
+                for (i=0; i < new_urls.length; i++) {
+                    if ($.inArray(new_urls[i].url, urls) < 0) {
+                        urls.push(new_urls[i].url);
+                    };
+                };
+            },
+            validate: function(new_url) {
+                // This can probably be optimized.
+                var new_urls = analyze_url(new_url),
+                    missing_urls = {},
+                    result = [],
+                    i, prefix, files;
+
+                for (i=0; i < new_urls.length; i++) {
+                    if ($.inArray(new_urls[i].url, urls) < 0) {
+                        prefix = new_urls[i].prefix;
+                        if (missing_urls[prefix] === undefined) {
+                            missing_urls[prefix] = [];
+                        };
+                        missing_urls[prefix].push(new_urls[i]);
+                    };
+                };
+                for (prefix in missing_urls) {
+                    if (missing_urls[prefix].length == 1 && missing_urls[prefix][0].file === null) {
+                        result.push(missing_urls[prefix][0].url);
+                    } else {
+                        files = [];
+                        for (i=0; i < missing_urls[prefix].length; i++) {
+                            files.push(missing_urls[prefix][i].file);
+                        };
+                        result.push([prefix, ':bundle:' + files.join(';')].join('/'));
+                    };
+                };
+                return result;
+            }
+        };
+    };
+
     var module = {
         /**
          * HTMLResourceManager, load extra JS or CSS at run time.
          */
         HTMLResourceManager: function($document) {
             var data = {
-                css: [],
-                js: []
+                css: HTMLResourceTracker(),
+                js: HTMLResourceTracker()
             };
             var root = $document.get(0);
             var head = root.getElementsByTagName('head')[0];
 
             var resources = {
                 /**
-                 * Return true if the given Javascript is already loaded.
-                 * @param script: url of the Javascript file.
+                 * Load given JS files.
+                 * @param script: list containing urls of the JS files.
                  */
-                is_js_loaded: function(script) {
-                    return $.inArray(script, data.js) > -1;
+                load_js: function(scripts) {
+                    var urls = infrae.utils.map_concat(scripts, data.js.validate);
+
+                    if (urls.length) {
+                        return $.when.apply($, infrae.utils.map(urls, function(url) {
+                            // We don't use jQuery here, as does strange things with scripts.
+                            var script_tag = root.createElement('script'),
+                                promise = $.Deferred();
+
+                            script_tag.async = 'async';
+                            script_tag.type = 'text/javascript';
+                            if (script_tag.readyState) {
+                                script_tag.onreadystatechange = function () {
+                                    if (script_tag.readyState == "loaded" ||
+                                        script_tag.readyState == "complete") {
+                                        script_tag.onreadystatechange = null;
+                                        promise.resolve();
+                                    };
+                                };
+                            } else {
+                                script_tag.onload = function() {
+                                    promise.resolve();
+                                };
+                            };
+                            script_tag.src = url;
+                            head.appendChild(script_tag);
+                            data.js.register(url);
+                            return promise;
+                        }));
+                    };
+                    return $.Deferred().resolve();
                 },
                 /**
-                 * Return true if the given CSS is already loaded.
-                 * @param css: url of the CSS file.
+                 * Load given CSS files.
+                 * @param css: list containing urls of the CSS files.
                  */
-                is_css_loaded: function(css) {
-                    return $.inArray(css, data.css) > -1;
-                },
-                /**
-                 * Load a given JS file.
-                 * @param script: url of the JS file.
-                 */
-                load_js: function(script) {
-                    var done = $.Deferred();
+                load_css: function(csses) {
+                    var urls = infrae.utils.map_concat(csses, data.css.validate);
 
-                    if (!resources.is_js_loaded(script)) {
-                        // We don't use jQuery here, as does strange things with scripts.
-                        var script_tag = root.createElement('script');
+                    if (urls.length) {
+                        return $.when.apply($, infrae.utils.map(urls, function(url) {
+                            // We don't use jQuery here, as does strange things with links.
+                            var link_tag = root.createElement('link');
 
-                        script_tag.async = 'async';
-                        script_tag.type = 'text/javascript';
-                        if (script_tag.readyState) {
-                            script_tag.onreadystatechange = function () {
-                                if (script_tag.readyState == "loaded" ||
-                                    script_tag.readyState == "complete") {
-                                    script_tag.onreadystatechange = null;
-                                    done.resolve();
-                                };
-                            };
-                        } else {
-                            script_tag.onload = function() {
-                                done.resolve();
-                            };
-                        };
-                        script_tag.src = script;
-                        head.appendChild(script_tag);
-                        data.js.push(script);
-                        return done;
+                            link_tag.rel = 'stylesheet';
+                            link_tag.type = 'text/css';
+                            link_tag.href = url;
+                            head.appendChild(link_tag);
+                            data.css.register(url);
+                            // We should use load event here too.
+                            return $.Deferred().resolve();
+                        }));
                     };
-                    return done.resolve();
-                },
-                /**
-                 * Load a given CSS file.
-                 * @param css: url of the CSS file.
-                 */
-                load_css: function(css) {
-                    if (!resources.is_css_loaded(css)) {
-                        // We don't use jQuery here, as does strange things with links.
-                        var link_tag = root.createElement('link');
-
-                        link_tag.rel = 'stylesheet';
-                        link_tag.type = 'text/css';
-                        link_tag.href = css;
-                        head.appendChild(link_tag);
-
-                        data.css.push(css);
-                    };
+                    return $.Deferred().resolve();
                 }
             };
 
@@ -89,8 +153,8 @@
                 $('script').each(function () {
                     var src = $(this).attr('src');
 
-                    if (src != undefined && ! resources.is_js_loaded(src)) {
-                        data.js.push(src);
+                    if (src != undefined) {
+                        data.js.register(src);
                     };
                 });
                 $('link').each(function () {
@@ -98,8 +162,8 @@
 
                     if ($link.attr('rel') == 'stylesheet' && $link.attr('type') == 'text/css') {
                         var src = $link.attr('href');
-                        if (src != undefined && !resources.is_css_loaded(src)) {
-                            data.css.push(src);
+                        if (src != undefined) {
+                            data.css.register(src);
                         };
                     };
                 });
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/infrae.views.min.js
--- a/src/silva/ui/static/js/infrae.views.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/infrae.views.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,infrae,jsontemplate){$.ajaxSetup({cache:!1});var module={HTMLResourceManager:function($document){var data={css:[],js:[]},root=$document.get(0),head=root.getElementsByTagName("head")[0],resources={is_js_loaded:function(script){return $.inArray(script,data.js)>-1},is_css_loaded:function(css){return $.inArray(css,data.css)>-1},load_js:function(script){var done=$.Deferred();if(!resources.is_js_loaded(script)){var script_tag=root.createElement("script");return script_tag.async="async",script_tag.type="text/javascript",script_tag.readyState?script_tag.onreadystatechange=function(){("loaded"==script_tag.readyState||"complete"==script_tag.readyState)&&(script_tag.onreadystatechange=null,done.resolve())}:script_tag.onload=function(){done.resolve()},script_tag.src=script,head.appendChil
 d(script_tag),data.js.push(script),done}return done.resolve()},load_css:function(css){if(!resources.is_css_loaded(css)){var link_tag=root.createElement("link");link_tag.rel="stylesheet",link
 _tag.type="text/css",link_tag.href=css,head.appendChild(link_tag),data.css.push(css)}}};return $document.ready(function(){$("script").each(function(){var src=$(this).attr("src");void 0==src||resources.is_js_loaded(src)||data.js.push(src)}),$("link").each(function(){var $link=$(this);if("stylesheet"==$link.attr("rel")&&"text/css"==$link.attr("type")){var src=$link.attr("href");void 0==src||resources.is_css_loaded(src)||data.css.push(src)}})}),resources}},template_cache={},View=function($content,data,factory,args,reject){var view={$content:$content,data:data};$.extend(view,factory.apply($content,args));var render=function(template){view.template_append||($content.triggerHandler("cleanup-infrae-views"),(view.cleanup||view.iframe)&&$content.one("cleanup-infrae-views",function(){view.cleanup&&
 view.cleanup(),view.iframe&&($(window).unbind("resize.infrae-views-iframe"),$(window).unbind("workspace-resize-smi.infrae-views-iframe"),$content.removeClass("iframe-view"))}));var deferred=
 $.Deferred(),$context=$content,resolve=function(){void 0!=reject?deferred.rejectWith(view,reject):deferred.resolveWith(view)},finalizer=function(){var done=null;return void 0!=view.render&&(done=view.render($context),done&&done.promise)?(done.promise().done(resolve),void 0):(resolve(),void 0)};if(view.iframe){if(view.template_append)throw{message:"Cannot use iframe and template_append together"};var $iframe=$('<iframe src="">');view.$iframe=$iframe;var resize=function(){var height=$content.innerHeight(),width=$content.innerWidth();view.resize_iframe&&view.resize_iframe(width,height)||($iframe.height(height),$iframe.width(width))};resize(),$(window).bind("resize.infrae-views-iframe",resize),$(window).bind("workspace-resize-smi.infrae-views-iframe",resize),$content.addClass("iframe-view"),$
 content.html($iframe);var iframe_window=$iframe.get(0).contentWindow,iframe_document=iframe_window.document;if(view.$window=$(iframe_window),view.$document=$(iframe_document),void 0!==templa
 te){view.ready=$.Deferred();var loader=function(){view.ready.resolve(view)};0>template.indexOf("<html")&&0>template.indexOf("<HTML")&&(template="<html><body>"+template+"</body></html>"),iframe_document.write(template),iframe_document.close(),"complete"==iframe_document.readyState?loader():iframe_window.addEventListener?iframe_window.addEventListener("load",loader,!1):iframe_window.attachEvent("onload",loader)}}else void 0!==template&&(view.template_append?($context=$(template),$content.append($context)):$content.html(template));return finalizer(),deferred.promise()};return function(){var template=void 0,jsont_url=view.template_data&&data.jsont_url||view.jsont_url;if(jsont_url)return view.template_nocache||(template=template_cache[jsont_url],void 0===template)?$.ajax({type:"GET",url:jsont_
 url}).then(function(payload){var template=new jsontemplate.Template(payload,{});return template_cache[jsont_url]=template,render(template.expand(data))}):render(template.expand(view));var ht
 ml_url=view.template_data&&data.html_url||view.html_url;if(html_url)return view.template_nocache||(template=template_cache[html_url],void 0===template)?$.ajax({type:"GET",url:html_url}).then(function(payload){return template_cache[html_url]=payload,render(payload)}):render(template);var jsont=view.template_data&&data.jsont||view.jsont;if(void 0!==jsont)return template=template_cache[jsont],void 0===template&&(template=new jsontemplate.Template(jsont,{}),template_cache[jsont]=template),render(template.expand(view));var html=view.template_data&&data.html||view.html;return void 0!==html?render(html):render()}};$.extend(module,{Registry:function(){var views={},count=0,render_best_view=function($content,data,options){var ifaces=options.ifaces||infrae.interfaces.implemented_by(data),named_views
 =views[options.name],to_render=null,args=[$content,data].concat(options.args),reject=options.reject;if(named_views)for(var i=0;ifaces.length>i;i++){var definitions=named_views[ifaces[i]];if(
 definitions&&definitions.length){to_render=View($content,data,definitions[0].factory,args,reject);break}}return null!=to_render?to_render():(window.console&&console.log&&console.log("failed view lookup for options",options,"and",data),$.Deferred().reject({status:604}))},render_all_views=function($content,data,options){var named_views=views[options.name];if(void 0==named_views)return[];var ifaces=options.ifaces||infrae.interfaces.implemented_by(data),seen_definitions=[],to_render=[],args=[$content,data].concat(options.args),reject=options.reject;return infrae.utils.each(ifaces,function(iface){var definitions=named_views[iface];definitions&&infrae.utils.each(definitions,function(definition){if(0>$.inArray(definition.__view_uid__,seen_definitions)){if(seen_definitions.push(definition.__view_
 uid__),void 0!=definition.available&&!definition.available.apply(definition,args))return;to_render.push([definition.order,View($content,data,definition.factory,args,reject)])}})}),to_render.
 sort(function(v1,v2){return v1[0]-v2[0]}),$.each(to_render,function(i,data){data[1]()}),null},render_url=function($content,url,options){return $.ajax({type:"GET",url:url,dataType:"json"}).then(function(data){return render_best_view($content,data,options)})},registry={register:function(definition){void 0==definition.name&&(definition.name="default"),void 0==definition.order&&(definition.order=0),definition.__view_uid__=""+count,count+=1;var add=function(definition,iface){views[definition.name]||(views[definition.name]={}),views[definition.name][iface]||(views[definition.name][iface]=[]),views[definition.name][iface].unshift(definition)};definition.ifaces?infrae.utils.each(definition.ifaces,function(iface){add(definition,iface)}):add(definition,definition.iface||"object")},render:function($
 content,options){if(options.name||(options.name="default"),options.data)return render_best_view($content,options.data,options);if(options.every)return render_all_views($content,options.every
 ,options);if(options.url)return render_url($content,options.url,options);throw{message:"Missing required options"}}};return registry}});var default_registry=module.Registry();$.extend(module,{view:function(definition){default_registry.register(definition)},render:function($content,options){return default_registry.render($content,options)}}),$.fn.render=function(options){return default_registry.render($(this),options)},infrae.views=module})(jQuery,infrae,jsontemplate);
\ No newline at end of file
+(function($,infrae,jsontemplate){var BUNDLE_RE=/^:bundle:/;$.ajaxSetup({cache:!1});var HTMLResourceTracker=function(){var urls=[],analyze_url=function(original){var parts=original.split("/"),last=parts[parts.length-1];if(last.match(BUNDLE_RE)){parts.pop();var i,files=last.substr(8).split(";"),prefix=parts.join("/"),result=[];for(i=0;files.length>i;i++)result.push({url:[prefix,files[i]].join("/"),file:files[i],prefix:prefix});return result}return[{url:original,file:null,prefix:original}]};return{register:function(new_url){var i,new_urls=analyze_url(new_url);for(i=0;new_urls.length>i;i++)0>$.inArray(new_urls[i].url,urls)&&urls.push(new_urls[i].url)},validate:function(new_url){var i,prefix,files,new_urls=analyze_url(new_url),missing_urls={},result=[];for(i=0;new_urls.length>i;i++)0>$.inArray
 (new_urls[i].url,urls)&&(prefix=new_urls[i].prefix,void 0===missing_urls[prefix]&&(missing_urls[prefix]=[]),missing_urls[prefix].push(new_urls[i]));for(prefix in missing_urls)if(1==missing_u
 rls[prefix].length&&null===missing_urls[prefix][0].file)result.push(missing_urls[prefix][0].url);else{for(files=[],i=0;missing_urls[prefix].length>i;i++)files.push(missing_urls[prefix][i].file);result.push([prefix,":bundle:"+files.join(";")].join("/"))}return result}}},module={HTMLResourceManager:function($document){var data={css:HTMLResourceTracker(),js:HTMLResourceTracker()},root=$document.get(0),head=root.getElementsByTagName("head")[0],resources={load_js:function(scripts){var urls=infrae.utils.map_concat(scripts,data.js.validate);return urls.length?$.when.apply($,infrae.utils.map(urls,function(url){var script_tag=root.createElement("script"),promise=$.Deferred();return script_tag.async="async",script_tag.type="text/javascript",script_tag.readyState?script_tag.onreadystatechange=functi
 on(){("loaded"==script_tag.readyState||"complete"==script_tag.readyState)&&(script_tag.onreadystatechange=null,promise.resolve())}:script_tag.onload=function(){promise.resolve()},script_tag.
 src=url,head.appendChild(script_tag),data.js.register(url),promise})):$.Deferred().resolve()},load_css:function(csses){var urls=infrae.utils.map_concat(csses,data.css.validate);return urls.length?$.when.apply($,infrae.utils.map(urls,function(url){var link_tag=root.createElement("link");return link_tag.rel="stylesheet",link_tag.type="text/css",link_tag.href=url,head.appendChild(link_tag),data.css.register(url),$.Deferred().resolve()})):$.Deferred().resolve()}};return $document.ready(function(){$("script").each(function(){var src=$(this).attr("src");void 0!=src&&data.js.register(src)}),$("link").each(function(){var $link=$(this);if("stylesheet"==$link.attr("rel")&&"text/css"==$link.attr("type")){var src=$link.attr("href");void 0!=src&&data.css.register(src)}})}),resources}},template_cache={
 },View=function($content,data,factory,args,reject){var view={$content:$content,data:data};$.extend(view,factory.apply($content,args));var render=function(template){view.template_append||($co
 ntent.triggerHandler("cleanup-infrae-views"),(view.cleanup||view.iframe)&&$content.one("cleanup-infrae-views",function(){view.cleanup&&view.cleanup(),view.iframe&&($(window).unbind("resize.infrae-views-iframe"),$(window).unbind("workspace-resize-smi.infrae-views-iframe"),$content.removeClass("iframe-view"))}));var deferred=$.Deferred(),$context=$content,resolve=function(){void 0!=reject?deferred.rejectWith(view,reject):deferred.resolveWith(view)},finalizer=function(){var done=null;return void 0!=view.render&&(done=view.render($context),done&&done.promise)?(done.promise().done(resolve),void 0):(resolve(),void 0)};if(view.iframe){if(view.template_append)throw{message:"Cannot use iframe and template_append together"};var $iframe=$('<iframe src="">');view.$iframe=$iframe;var resize=function()
 {var height=$content.innerHeight(),width=$content.innerWidth();view.resize_iframe&&view.resize_iframe(width,height)||($iframe.height(height),$iframe.width(width))};resize(),$(window).bind("r
 esize.infrae-views-iframe",resize),$(window).bind("workspace-resize-smi.infrae-views-iframe",resize),$content.addClass("iframe-view"),$content.html($iframe);var iframe_window=$iframe.get(0).contentWindow,iframe_document=iframe_window.document;if(view.$window=$(iframe_window),view.$document=$(iframe_document),void 0!==template){view.ready=$.Deferred();var loader=function(){view.ready.resolve(view)};0>template.indexOf("<html")&&0>template.indexOf("<HTML")&&(template="<html><body>"+template+"</body></html>"),iframe_document.write(template),iframe_document.close(),"complete"==iframe_document.readyState?loader():iframe_window.addEventListener?iframe_window.addEventListener("load",loader,!1):iframe_window.attachEvent("onload",loader)}}else void 0!==template&&(view.template_append?($context=$(te
 mplate),$content.append($context)):$content.html(template));return finalizer(),deferred.promise()};return function(){var template=void 0,jsont_url=view.template_data&&data.jsont_url||view.js
 ont_url;if(jsont_url)return view.template_nocache||(template=template_cache[jsont_url],void 0===template)?$.ajax({type:"GET",url:jsont_url}).then(function(payload){var template=new jsontemplate.Template(payload,{});return template_cache[jsont_url]=template,render(template.expand(data))}):render(template.expand(view));var html_url=view.template_data&&data.html_url||view.html_url;if(html_url)return view.template_nocache||(template=template_cache[html_url],void 0===template)?$.ajax({type:"GET",url:html_url}).then(function(payload){return template_cache[html_url]=payload,render(payload)}):render(template);var jsont=view.template_data&&data.jsont||view.jsont;if(void 0!==jsont)return template=template_cache[jsont],void 0===template&&(template=new jsontemplate.Template(jsont,{}),template_cache[j
 sont]=template),render(template.expand(view));var html=view.template_data&&data.html||view.html;return void 0!==html?render(html):render()}};$.extend(module,{Registry:function(){var views={}
 ,count=0,render_best_view=function($content,data,options){var ifaces=options.ifaces||infrae.interfaces.implemented_by(data),named_views=views[options.name],to_render=null,args=[$content,data].concat(options.args),reject=options.reject;if(named_views)for(var i=0;ifaces.length>i;i++){var definitions=named_views[ifaces[i]];if(definitions&&definitions.length){to_render=View($content,data,definitions[0].factory,args,reject);break}}return null!=to_render?to_render():(window.console&&console.log&&console.log("failed view lookup for options",options,"and",data),$.Deferred().reject({status:604}))},render_all_views=function($content,data,options){var named_views=views[options.name];if(void 0==named_views)return[];var ifaces=options.ifaces||infrae.interfaces.implemented_by(data),seen_definitions=[],
 to_render=[],args=[$content,data].concat(options.args),reject=options.reject;return infrae.utils.each(ifaces,function(iface){var definitions=named_views[iface];definitions&&infrae.utils.each
 (definitions,function(definition){if(0>$.inArray(definition.__view_uid__,seen_definitions)){if(seen_definitions.push(definition.__view_uid__),void 0!=definition.available&&!definition.available.apply(definition,args))return;to_render.push([definition.order,View($content,data,definition.factory,args,reject)])}})}),to_render.sort(function(v1,v2){return v1[0]-v2[0]}),$.each(to_render,function(i,data){data[1]()}),null},render_url=function($content,url,options){return $.ajax({type:"GET",url:url,dataType:"json"}).then(function(data){return render_best_view($content,data,options)})},registry={register:function(definition){void 0==definition.name&&(definition.name="default"),void 0==definition.order&&(definition.order=0),definition.__view_uid__=""+count,count+=1;var add=function(definition,iface)
 {views[definition.name]||(views[definition.name]={}),views[definition.name][iface]||(views[definition.name][iface]=[]),views[definition.name][iface].unshift(definition)};definition.ifaces?in
 frae.utils.each(definition.ifaces,function(iface){add(definition,iface)}):add(definition,definition.iface||"object")},render:function($content,options){if(options.name||(options.name="default"),options.data)return render_best_view($content,options.data,options);if(options.every)return render_all_views($content,options.every,options);if(options.url)return render_url($content,options.url,options);throw{message:"Missing required options"}}};return registry}});var default_registry=module.Registry();$.extend(module,{view:function(definition){default_registry.register(definition)},render:function($content,options){return default_registry.render($content,options)}}),$.fn.render=function(options){return default_registry.render($(this),options)},infrae.views=module})(jQuery,infrae,jsontemplate);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/listing.items.js
--- a/src/silva/ui/static/js/listing.items.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/listing.items.js	Thu Sep 26 14:33:42 2013 +0200
@@ -242,7 +242,7 @@
                     } else if (initial) {
                         // Add a message no lines.
                         api.$table.append(
-                            '<tr class="empty"><td colpsan="' + configuration.columns.length + '">There is no items here.</td></tr>');
+                            '<tr class="empty"><td colpsan="' + configuration.columns.length + '">There are no items here.</td></tr>');
                     };
                     deferred.resolve([]);
                     return deferred.promise();
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/listing.items.min.js
--- a/src/silva/ui/static/js/listing.items.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/listing.items.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,infrae){var toggle_header=function($header,$container,configuration){var $marker=$header.children("ins.ui-icon"),toggle_marker=function(){$marker.hasClass("ui-icon-triangle-1-e")?($marker.removeClass("ui-icon-triangle-1-e"),$marker.addClass("ui-icon-triangle-1-s")):($marker.removeClass("ui-icon-triangle-1-s"),$marker.addClass("ui-icon-triangle-1-e"))},toggle_collapsing=function(){configuration.collapsed=!configuration.collapsed,toggle_marker(),$header.toggleClass("collapsed"),$container.toggleClass("collapsed"),$header.trigger("collapsingchange-smilisting")};configuration.collapsed&&(toggle_marker(),$header.addClass("collapsed"),$container.addClass("collapsed"),$header.trigger("collapsingchange-smilisting")),$header.bind("click",function(){return toggle_collapsing(),!1})},sort
 _items=function($container,$table,callback){var original=null;$container.tableDnD({dragHandle:"moveable",onDragClass:"moving",onAllowDrop:function(row,candidate){return 0!=$(candidate).data(
 "smilisting").moveable},onDragStart:function(row){return $table.hasClass("filtering")?!1:(original=$(row).parent("tr").index(),$table.trigger("resetselection-smilisting"),$table.removeClass("static"),void 0)},onDrop:function(row){var $line=$(row),position=$line.index(),data=$line.data("smilisting");position!=original?(position>original&&(original-=1),$container.children("tr.item:first").data("smilisting").moveable||(position-=1),callback([{name:"content",value:data.id},{name:"position",value:position}]).then(function(success){return success||(original>position&&(original-=1),$line.detach(),$line.insertAfter($container.children("tr.item:eq("+original+")"))),$table.addClass("static"),success})):$table.addClass("static")}})};infrae.views.view({iface:"listing-items",name:"listing-container",f
 actory:function($listing,data,configuration,api){var insert_line=function($line,data,position){if(position>-1){var $after=api.$container.children().eq(position);$after.length?$after.before($
 line):api.$container.append($line)}else api.$container.append($line)},render_line=function(data){var $line=$('<tr class="item"></tr>'),line={};$line.attr("id","list"+(""+data.id));var render_cell=function(column){var $cell=$("<td></td>");column.view&&($cell.bind("refreshcell-smilisting",function(event,data){var $cell=$(this),value=null;column.name&&(value=data[column.name]),infrae.smi.listing.columns.render($cell,{data:data,name:column.view,ifaces:["object"],args:[column,value]}),event.stopPropagation(),event.preventDefault()}),void 0!==column.name&&void 0!==column.renameable&&($cell.addClass("renameable"),$cell.bind("renamecell-smilisting",function(event,data){if(event.stopPropagation(),event.preventDefault(),void 0===column.renameable.item_match||infrae.utils.test(data,column.renameable
 .item_match)){var $cell=$(this),$field=$('<input class="renaming" type="text" />');$field.attr("name",column.name),$field.val(data[column.name]),$cell.empty(),$cell.append($field)}}))),$line
 .append($cell)};return infrae.utils.each(configuration.columns,render_cell),$.extend(line,{update:function(data){if(void 0!=data){if($line.data("smilisting",data),data.position>-1){var new_position=data.position,$first=api.$container.children("tr.item:first");$first.length&&$first.data("smilisting").moveable&&(new_position-=1),new_position>0&&api.$container.children().index($line)!=new_position&&($line.detach(),insert_line($line,data,new_position))}}else data=$line.data("smilisting");$line.children().each(function(){$(this).triggerHandler("refreshcell-smilisting",data)})},rename:function(){var data=$line.data("smilisting");$line.find("td.renameable").each(function(){$(this).triggerHandler("renamecell-smilisting",data)})},values:function(){var $inputs=$line.find("input.renaming");if(!$inpu
 ts.length)return void 0;var data=$line.data("smilisting"),values={id:data.id};return $inputs.each(function(){var $input=$(this);values[$input.attr("name")]=$input.val()}),values}}),line.upda
 te(data),$line.data("smilisting-line",line),insert_line($line,data,data.position),$line.get(0)};return $.extend(api,{title:configuration.title,add:function(lines,initial){var deferred=$.Deferred();return lines.length?(initial||api.$table.children(".empty").remove(),api.$table.prepend("<colgroup>"+Array(configuration.columns.length+1).join("<col></col>")+"</colgroup>"),this.add=function(lines){return api.worker.add(lines,render_line)},api.add(lines)):(initial&&api.$table.append('<tr class="empty"><td colpsan="'+configuration.columns.length+'">There is no items here.</td></tr>'),deferred.resolve([]),deferred.promise())},$container:null,$table:null}),$.extend({template_append:!0,jsont:'<dt class="ui-state-default"><ins class="ui-icon ui-icon-triangle-1-s"></ins>{title}</dt><dd><table class="
 static"><tbody></tbody></table></dd>',render:function($content){return api.$table=$content.find("table"),api.$container=api.$table.find("tbody"),toggle_header($content.filter("dt"),$content.
 filter("dd"),configuration),api.sort&&sort_items(api.$container,api.$table,api.sort),api.add(data.items,!0)}},api)}})})(jQuery,infrae,jsontemplate);
\ No newline at end of file
+(function($,infrae){var toggle_header=function($header,$container,configuration){var $marker=$header.children("ins.ui-icon"),toggle_marker=function(){$marker.hasClass("ui-icon-triangle-1-e")?($marker.removeClass("ui-icon-triangle-1-e"),$marker.addClass("ui-icon-triangle-1-s")):($marker.removeClass("ui-icon-triangle-1-s"),$marker.addClass("ui-icon-triangle-1-e"))},toggle_collapsing=function(){configuration.collapsed=!configuration.collapsed,toggle_marker(),$header.toggleClass("collapsed"),$container.toggleClass("collapsed"),$header.trigger("collapsingchange-smilisting")};configuration.collapsed&&(toggle_marker(),$header.addClass("collapsed"),$container.addClass("collapsed"),$header.trigger("collapsingchange-smilisting")),$header.bind("click",function(){return toggle_collapsing(),!1})},sort
 _items=function($container,$table,callback){var original=null;$container.tableDnD({dragHandle:"moveable",onDragClass:"moving",onAllowDrop:function(row,candidate){return 0!=$(candidate).data(
 "smilisting").moveable},onDragStart:function(row){return $table.hasClass("filtering")?!1:(original=$(row).parent("tr").index(),$table.trigger("resetselection-smilisting"),$table.removeClass("static"),void 0)},onDrop:function(row){var $line=$(row),position=$line.index(),data=$line.data("smilisting");position!=original?(position>original&&(original-=1),$container.children("tr.item:first").data("smilisting").moveable||(position-=1),callback([{name:"content",value:data.id},{name:"position",value:position}]).then(function(success){return success||(original>position&&(original-=1),$line.detach(),$line.insertAfter($container.children("tr.item:eq("+original+")"))),$table.addClass("static"),success})):$table.addClass("static")}})};infrae.views.view({iface:"listing-items",name:"listing-container",f
 actory:function($listing,data,configuration,api){var insert_line=function($line,data,position){if(position>-1){var $after=api.$container.children().eq(position);$after.length?$after.before($
 line):api.$container.append($line)}else api.$container.append($line)},render_line=function(data){var $line=$('<tr class="item"></tr>'),line={};$line.attr("id","list"+(""+data.id));var render_cell=function(column){var $cell=$("<td></td>");column.view&&($cell.bind("refreshcell-smilisting",function(event,data){var $cell=$(this),value=null;column.name&&(value=data[column.name]),infrae.smi.listing.columns.render($cell,{data:data,name:column.view,ifaces:["object"],args:[column,value]}),event.stopPropagation(),event.preventDefault()}),void 0!==column.name&&void 0!==column.renameable&&($cell.addClass("renameable"),$cell.bind("renamecell-smilisting",function(event,data){if(event.stopPropagation(),event.preventDefault(),void 0===column.renameable.item_match||infrae.utils.test(data,column.renameable
 .item_match)){var $cell=$(this),$field=$('<input class="renaming" type="text" />');$field.attr("name",column.name),$field.val(data[column.name]),$cell.empty(),$cell.append($field)}}))),$line
 .append($cell)};return infrae.utils.each(configuration.columns,render_cell),$.extend(line,{update:function(data){if(void 0!=data){if($line.data("smilisting",data),data.position>-1){var new_position=data.position,$first=api.$container.children("tr.item:first");$first.length&&$first.data("smilisting").moveable&&(new_position-=1),new_position>0&&api.$container.children().index($line)!=new_position&&($line.detach(),insert_line($line,data,new_position))}}else data=$line.data("smilisting");$line.children().each(function(){$(this).triggerHandler("refreshcell-smilisting",data)})},rename:function(){var data=$line.data("smilisting");$line.find("td.renameable").each(function(){$(this).triggerHandler("renamecell-smilisting",data)})},values:function(){var $inputs=$line.find("input.renaming");if(!$inpu
 ts.length)return void 0;var data=$line.data("smilisting"),values={id:data.id};return $inputs.each(function(){var $input=$(this);values[$input.attr("name")]=$input.val()}),values}}),line.upda
 te(data),$line.data("smilisting-line",line),insert_line($line,data,data.position),$line.get(0)};return $.extend(api,{title:configuration.title,add:function(lines,initial){var deferred=$.Deferred();return lines.length?(initial||api.$table.children(".empty").remove(),api.$table.prepend("<colgroup>"+Array(configuration.columns.length+1).join("<col></col>")+"</colgroup>"),this.add=function(lines){return api.worker.add(lines,render_line)},api.add(lines)):(initial&&api.$table.append('<tr class="empty"><td colpsan="'+configuration.columns.length+'">There are no items here.</td></tr>'),deferred.resolve([]),deferred.promise())},$container:null,$table:null}),$.extend({template_append:!0,jsont:'<dt class="ui-state-default"><ins class="ui-icon ui-icon-triangle-1-s"></ins>{title}</dt><dd><table class=
 "static"><tbody></tbody></table></dd>',render:function($content){return api.$table=$content.find("table"),api.$container=api.$table.find("tbody"),toggle_header($content.filter("dt"),$content
 .filter("dd"),configuration),api.sort&&sort_items(api.$container,api.$table,api.sort),api.add(data.items,!0)}},api)}})})(jQuery,infrae,jsontemplate);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.form.js
--- a/src/silva/ui/static/js/smi.form.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.form.js	Thu Sep 26 14:33:42 2013 +0200
@@ -2,119 +2,155 @@
 
 (function($, infrae) {
 
-    // This is not really usefull, we could take this as a parameter.
-    $.fn.invoke = function() {
-        return Array.prototype.splice.call(arguments, 0, 1)[0].apply(this, arguments);
-    };
+    /**
+     * Scroll the whole field (i.e. its title, description and all
+     * input widgets) into view inside the $content area.
+     */
+    var scroll_field_into_view = function($content, $field) {
+        var $section = $field.closest('.form-section');
 
-    // Scroll field into view.
-    var scroll_field_into_view = function($field) {
-        if (!$field.length) {
+        if (!$section.length) {
             return;
         };
 
-        var top = $field.position().top;
-        var height = $field.outerHeight();
-        var $container = $(this);
-        var target, limit;
+        var section_top = $section.position().top,
+            section_height = $section.outerHeight(),
+            section_bottom = section_top + section_height,
+            content_top = $content.scrollTop(),
+            content_height = $content.innerHeight(),
+            content_bottom = content_top + content_height;
 
-        if (top < 25) {
-            target = $container.scrollTop() - 25 + top;
-            infrae.ui.scroll($container, 'fast', 'absolute', target);
-        } else {
-            limit = $container.innerHeight() - height - 25;
-            if (top > limit) {
-                target = $container.scrollTop() + (top - limit);
-                infrae.ui.scroll($container, 'fast', 'absolute', target);
-            };
+        if ((section_top - 25) < content_top) {
+            infrae.ui.scroll($content, 'fast', 'absolute', section_top - 25);
+        } else if (content_bottom < (section_bottom + 25)) {
+            infrae.ui.scroll($content, 'fast', 'absolute', section_bottom - content_height + 25);
         };
     };
 
-    // Unfocus all field.
-    var unfocus_form_fields = function() {
-        $(this).find('.form-section').removeClass('form-focus');
+    /**
+     * Unfocus every form fields in the given $content area.
+     */
+    var unfocus_form_fields = function($content) {
+        $content.find('.form-section').removeClass('form-focus');
     };
 
-    // Focus form field.
-    var focus_form_field = function($field, no_input_focus) {
+    /**
+     * Focus the given form $field in the given $content
+     * area. Optionally send a focus event to the form widget
+     * contained in this field.
+     */
+    var focus_form_field = function($content, $field, no_input_focus) {
         var $section = $field.closest('.form-section');
 
         if ($section.is('.form-focus')) {
             return;
         };
-        $(this).invoke(unfocus_form_fields);
+        unfocus_form_fields($content);
         $section.addClass('form-focus');
         if (!no_input_focus) {
             $section.find('.field:first').focus();
         };
     };
-    var focus_first_form_field = function() {
-        var $forms = $(this);
-        var $field = $forms.find('.form-error:first').find('.field:first');
+
+    /**
+     * Focus the first required field or the first field with an error
+     * in the given $content area. It will scroll in place for the
+     * field.
+     *
+     * This is called when the form is initialized.
+     */
+    var focus_first_form_field = function($content) {
+        var $field = $content.find('.form-error:first').find('.field:first');
 
         if (!$field.length){
-            $field = $forms.find('.field-required:first');
-            if (!$field.length)
-                $field = $forms.find('.field:first');
+            $field = $content.find('.field-required:first');
+            if (!$field.length) {
+                $field = $content.find('.field:first');
+            }
         };
         if ($field.length) {
-            $forms.invoke(focus_form_field, $field);
-            $forms.invoke(scroll_field_into_view, $field.closest('.form-section'));
+            focus_form_field($content, $field);
+            scroll_field_into_view($content, $field);
         };
     };
-    var focus_next_form_field = function() {
-        var $forms = $(this);
-        var $focused = $forms.find('.form-focus');
-        var $field = $focused.next();
+
+    /**
+     * Inside the given $content area find and focus the next field
+     *  located after the currently focused one.
+     *
+     * This is called by the keyboard shortcuts handler (ctrl+down).
+     */
+    var focus_next_form_field = function($content) {
+        var $focused = $content.find('.form-focus'),
+            $field = $focused.next(),
+            $next_body, $next_form;
 
         if (!$field.length) {
-            var $next_body = $focused.closest('.form-body').nextAll('.form-body');
+            // We didn't find a field in this form, try the next form.
+            $next_body = $focused.closest('.form-body').nextAll('.form-body');
+
             if ($next_body.length)
                 $field = $next_body.find('.form-section:first');
             else {
-                var $next_form = $focused.closest('form').nextAll('form');
+                $next_form = $focused.closest('form').nextAll('form');
+
                 if ($next_form.length)
                     $field = $next_form.find('.form-section:first');
                 else
-                    $field = $forms.find('.form-section:first');
+                    $field = $content.find('.form-section:first');
             };
         };
-        $forms.invoke(focus_form_field, $field);
-        $forms.invoke(scroll_field_into_view, $field);
+        focus_form_field($content, $field);
+        scroll_field_into_view($content, $field);
     };
-    var focus_previous_form_field = function() {
-        var $forms = $(this);
-        var $focused = $forms.find('.form-focus');
-        var $field = $focused.prev();
+
+    /**
+     * Inside the given $content area find and focus the previous
+     * field located before the currently focused one.
+     *
+     * This is called by the keyboard shortcuts handler (ctrl+up).
+     */
+    var focus_previous_form_field = function($content) {
+        var $focused = $content.find('.form-focus'),
+            $field = $focused.prev(),
+            $prev_body, $prev_form;
 
         if (!$field.length) {
-            var $prev_body = $focused.closest('.form-body').prevAll('.form-body');
+            $prev_body = $focused.closest('.form-body').prevAll('.form-body');
+
             if ($prev_body.length)
                 $field = $prev_body.find('.form-section:last');
             else {
-                var $prev_form = $focused.closest('form').prevAll('form');
+                $prev_form = $focused.closest('form').prevAll('form');
+
                 if ($prev_form.length)
                     $field = $prev_form.find('.form-section:last');
                 else
-                    $field = $forms.find('.form-section:last');
+                    $field = $content.find('.form-section:last');
             };
         };
-        $forms.invoke(focus_form_field, $field);
-        $forms.invoke(scroll_field_into_view, $field);
+        focus_form_field($content, $field);
+        scroll_field_into_view($content, $field);
     };
 
-    // Bind form helpers: focus changes, cleanup error, load widgets JS
+    /**
+     * Initialize a form upon an event. The form can be defined here
+     * with the form view, but can be a popup or an add form inside a
+     * reference widget too. This will bind focus events and
+     * initialize widgets.
+     */
     $(document).on('load-smiform', '.form-content', function(event, data) {
-        var $container = data.container || $(this);
-        var $form = data.form || $container.find('form');
+        var $container = data.container || $(this),
+            $content = data.$content || $container,
+            $form = data.form || $container.find('form');
 
         // Bind form focus
         var focus_form_event = function (event) {
-            var $target = $(event.target);
-            var $section = $target.closest('.form-section');
-            var is_cancelable = $target.is('input') || $target.is('a');
+            var $target = $(event.target),
+                $section = $target.closest('.form-section'),
+                is_cancelable = $target.is('input') || $target.is('a');
 
-            $container.invoke(focus_form_field, $section, is_cancelable);
+            focus_form_field($content, $section, is_cancelable);
             if (!is_cancelable) {
                 event.stopPropagation();
             };
@@ -129,7 +165,7 @@
         });
 
         // Focus the first field of the first form.
-        $container.invoke(focus_first_form_field);
+        focus_first_form_field($content);
         // Load the widgets.
         $form.trigger('loadwidget-smiform', data);
     });
@@ -138,90 +174,107 @@
         iface: 'form',
         name: 'content',
         factory: function($content, data, smi) {
-            var $content_form = null;
-            var $forms = $([]);
+            var $container = null,
+                $form = $([]),
+                objection = null;
 
             return {
                 template_data: true,
                 render: function() {
                     // Add content
                     if (data.portlets) {
-                        var $content_portlets = $('<div class="portlets form-content"></div>');
+                        var $portlets = $('<div class="portlets form-content"></div>');
 
-                        $content_form = $('<div class="forms form-content"></div>');
-                        $content_portlets.html(data.portlets);
-                        $content.append($content_form);
-                        $content.append($content_portlets);
+                        $container = $('<div class="forms form-content"></div>');
+                        $portlets.html(data.portlets);
+                        $content.append($container);
+                        $content.append($portlets);
                     } else {
-                        $content_form = $content;
-                        $content_form.addClass('form-content');
+                        $container = $content;
+                        $container.addClass('form-content');
                     };
-                    // Find forms.
-                    $content_form.html(data.forms);
-                    $forms = $content_form.find('form');
-
-                    smi.shortcuts.create('form', $content_form, true);
+                    // Find forms
+                    $container.html(data.forms);
+                    smi.shortcuts.create('form', $container, true);
 
                     // Initialize each form.
-                    $forms.each(function() {
+                    $form = $container.find('form');
+                    $form.each(function() {
                         var $form = $(this);
                         var form_prefix = $form.attr('name');
 
-                        var make_submit = function($control) {
-                            var message = $control.attr('data-confirmation');
+                        var submit = function($control) {
+                            return function() {
+                                var queue = $.Deferred(),
+                                    submission = queue,
+                                    have_control = $control !== undefined && $control.length,
+                                    message = undefined,
+                                    is_link = false;
 
-                            if (message !== undefined) {
-                                var title = $control.attr('data-confirmation-title');
+                                var serialize = function() {
+                                    var values;
 
-                                // If there is a message, ask confirmation before triggering the action
-                                if (!title) {
-                                    title = 'Please confirm';
+                                    $form.trigger('serialize-smiform', {form: $form, container: $container});
+                                    values = $form.serializeArray();
+                                    if (have_control) {
+                                        values.push({
+                                            name: $control.attr('name'),
+                                            value: $control.text()
+                                        });
+                                    };
+                                    return values;
                                 };
-                                return function () {
-                                    infrae.ui.ConfirmationDialog({title:title, message:message}).then(function() {
-                                        return submit($control);
+
+                                if (have_control) {
+                                    message = $control.attr('data-confirmation');
+                                    is_link = $control.hasClass('link-control');
+                                };
+                                if (is_link) {
+                                    // If action is a link control, it will open in a different target.
+                                    $control.attr(
+                                        'href',
+                                        [smi.get_screen_url(), '?', $.param(serialize())].join(''));
+                                } else {
+                                    if (message !== undefined) {
+                                        var title = $control.attr('data-confirmation-title');
+
+                                        // If there is a message, ask confirmation before triggering the action
+                                        if (!title) {
+                                            title = 'Please confirm';
+                                        };
+                                        queue = queue.then(function() {
+                                            return infrae.ui.ConfirmationDialog({title:title, message:message});
+                                        });
+                                    };
+                                    queue.then(function() {
+                                        if (objection !== null) {
+                                            var result = objection();
+                                            if (result) {
+                                                return result.done(function() {
+                                                    objection = null;
+                                                });
+                                            };
+                                        };
+                                        return $.Deferred().resolve();
+                                    }, function() {
+                                        return $.Deferred().reject();
+                                    }).then(function () {
+                                        return smi.ajax.send_to_opened(serialize());
                                     });
+                                    submission.resolve();
                                     return false;
                                 };
                             };
-                            // Trigger directly the action.
-                            return function () {
-                                return submit($control);
-                            };
-                        };
-
-                        var submit = function($control) {
-                            $form.trigger('serialize-smiform', {form: $form, container: $content_form});
-
-                            var values = $form.serializeArray();
-                            var is_link = false;
-
-                            if ($control !== undefined && $control.length) {
-                                values.push({
-                                    name: $control.attr('name'),
-                                    value: $control.text()
-                                });
-                                is_link = $control.hasClass('link-control');
-                            };
-                            if (is_link) {
-                                // If action is a link control, it will open in a different target.
-                                $control.attr(
-                                    'href',
-                                    [smi.get_screen_url(), '?', $.param(values)].join(''));
-                            } else {
-                                smi.ajax.send_to_opened(values);
-                                return false;
-                            };
                         };
 
                         // Bind default submit and refresh
-                        $form.bind('submit', make_submit($form.find('.form-controls a.default-form-control')));
+                        $form.bind('submit', submit($form.find('.form-controls a.default-form-control')));
 
                         // Bind click submit
                         $form.find('.form-controls a.form-control').each(function () {
                             var $control = $(this),
                                 shortcut = $control.attr('data-form-shortcut'),
-                                handler = make_submit($control);
+                                handler = submit($control);
 
                             $control.bind('click', handler);
                             if (shortcut) {
@@ -240,11 +293,12 @@
                             };
                         });
                         $form.delegate('.form-controls a.form-popup', 'click', function () {
-                            var $control = $(this);
+                            var $control = $(this),
+                                handler = submit();
 
                             $control.SMIFormPopup().done(function (data) {
                                 if (data.extra && data.extra.refresh == form_prefix) {
-                                    submit();
+                                    handler();
                                 };
                             });
                             return false;
@@ -259,22 +313,28 @@
 
                     });
                     // Send an event form loaded to init specific JS field
-                    $content_form.trigger('load-smiform', {form: $forms, container: $content_form});
+                    $container.trigger('load-smiform', {
+                        form: $form,
+                        container: $container,
+                        $content: $content,
+                        set_objection: function(deferred) {
+                            objection = deferred;
+                        }});
 
                     // Shortcuts field navigation
                     smi.shortcuts.bind('form', null, ['ctrl+down', 'ctrl+shift+down'], function() {
-                        $content_form.invoke(focus_next_form_field);
+                        focus_next_form_field($content);
                         return false;
                     });
                     smi.shortcuts.bind('form', null, ['ctrl+up', 'ctrl+shift+up'], function() {
-                        $content_form.invoke(focus_previous_form_field);
+                        focus_previous_form_field($content);
                         return false;
                     });
                 },
                 cleanup: function() {
                     // Send cleanup event
-                    if ($content_form !== null) {
-                        $content_form.trigger('clean-smiform', {form: $forms, container: $content_form});
+                    if ($container !== null) {
+                        $container.trigger('clean-smiform', {form: $form, container: $container});
                     };
                     // Cleanup
                     smi.shortcuts.remove('form');
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.form.min.js
--- a/src/silva/ui/static/js/smi.form.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.form.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,infrae){$.fn.invoke=function(){return Array.prototype.splice.call(arguments,0,1)[0].apply(this,arguments)};var scroll_field_into_view=function($field){if($field.length){var target,limit,top=$field.position().top,height=$field.outerHeight(),$container=$(this);25>top?(target=$container.scrollTop()-25+top,infrae.ui.scroll($container,"fast","absolute",target)):(limit=$container.innerHeight()-height-25,top>limit&&(target=$container.scrollTop()+(top-limit),infrae.ui.scroll($container,"fast","absolute",target)))}},unfocus_form_fields=function(){$(this).find(".form-section").removeClass("form-focus")},focus_form_field=function($field,no_input_focus){var $section=$field.closest(".form-section");$section.is(".form-focus")||($(this).invoke(unfocus_form_fields),$section.addClass("form-foc
 us"),no_input_focus||$section.find(".field:first").focus())},focus_first_form_field=function(){var $forms=$(this),$field=$forms.find(".form-error:first").find(".field:first");$field.length||
 ($field=$forms.find(".field-required:first"),$field.length||($field=$forms.find(".field:first"))),$field.length&&($forms.invoke(focus_form_field,$field),$forms.invoke(scroll_field_into_view,$field.closest(".form-section")))},focus_next_form_field=function(){var $forms=$(this),$focused=$forms.find(".form-focus"),$field=$focused.next();if(!$field.length){var $next_body=$focused.closest(".form-body").nextAll(".form-body");if($next_body.length)$field=$next_body.find(".form-section:first");else{var $next_form=$focused.closest("form").nextAll("form");$field=$next_form.length?$next_form.find(".form-section:first"):$forms.find(".form-section:first")}}$forms.invoke(focus_form_field,$field),$forms.invoke(scroll_field_into_view,$field)},focus_previous_form_field=function(){var $forms=$(this),$focuse
 d=$forms.find(".form-focus"),$field=$focused.prev();if(!$field.length){var $prev_body=$focused.closest(".form-body").prevAll(".form-body");if($prev_body.length)$field=$prev_body.find(".form-
 section:last");else{var $prev_form=$focused.closest("form").prevAll("form");$field=$prev_form.length?$prev_form.find(".form-section:last"):$forms.find(".form-section:last")}}$forms.invoke(focus_form_field,$field),$forms.invoke(scroll_field_into_view,$field)};$(document).on("load-smiform",".form-content",function(event,data){var $container=data.container||$(this),$form=data.form||$container.find("form"),focus_form_event=function(event){var $target=$(event.target),$section=$target.closest(".form-section"),is_cancelable=$target.is("input")||$target.is("a");$container.invoke(focus_form_field,$section,is_cancelable),is_cancelable||event.stopPropagation()};$container.delegate(".form-section","click",focus_form_event),$container.delegate(".form-section","focusin",focus_form_event),$container.del
 egate(".form-error-detail","click",function(){$(this).fadeOut().promise().done(function(){$(this).remove()})}),$container.invoke(focus_first_form_field),$form.trigger("loadwidget-smiform",da
 ta)}),infrae.views.view({iface:"form",name:"content",factory:function($content,data,smi){var $content_form=null,$forms=$([]);return{template_data:!0,render:function(){if(data.portlets){var $content_portlets=$('<div class="portlets form-content"></div>');$content_form=$('<div class="forms form-content"></div>'),$content_portlets.html(data.portlets),$content.append($content_form),$content.append($content_portlets)}else $content_form=$content,$content_form.addClass("form-content");$content_form.html(data.forms),$forms=$content_form.find("form"),smi.shortcuts.create("form",$content_form,!0),$forms.each(function(){var $form=$(this),form_prefix=$form.attr("name"),make_submit=function($control){var message=$control.attr("data-confirmation");if(void 0!==message){var title=$control.attr("data-conf
 irmation-title");return title||(title="Please confirm"),function(){return infrae.ui.ConfirmationDialog({title:title,message:message}).then(function(){return submit($control)}),!1}}return fun
 ction(){return submit($control)}},submit=function($control){$form.trigger("serialize-smiform",{form:$form,container:$content_form});var values=$form.serializeArray(),is_link=!1;return void 0!==$control&&$control.length&&(values.push({name:$control.attr("name"),value:$control.text()}),is_link=$control.hasClass("link-control")),is_link?($control.attr("href",[smi.get_screen_url(),"?",$.param(values)].join("")),void 0):(smi.ajax.send_to_opened(values),!1)};$form.bind("submit",make_submit($form.find(".form-controls a.default-form-control"))),$form.find(".form-controls a.form-control").each(function(){var $control=$(this),shortcut=$control.attr("data-form-shortcut"),handler=make_submit($control);$control.bind("click",handler),shortcut&&smi.shortcuts.bind("form",null,[shortcut],handler)}),$form.
 find(".form-controls a.form-button").each(function(){var $control=$(this),shortcut=$control.attr("data-form-shortcut");shortcut&&smi.shortcuts.bind("form",null,[shortcut],function(){return $
 control.click(),!1})}),$form.delegate(".form-controls a.form-popup","click",function(){var $control=$(this);return $control.SMIFormPopup().done(function(data){data.extra&&data.extra.refresh==form_prefix&&submit()}),!1}),$form.tipsy({delegate:"a.form-control"}),$form.tipsy({delegate:"a.form-button"}),$form.attr("data-form-url",smi.get_screen_url())}),$content_form.trigger("load-smiform",{form:$forms,container:$content_form}),smi.shortcuts.bind("form",null,["ctrl+down","ctrl+shift+down"],function(){return $content_form.invoke(focus_next_form_field),!1}),smi.shortcuts.bind("form",null,["ctrl+up","ctrl+shift+up"],function(){return $content_form.invoke(focus_previous_form_field),!1})},cleanup:function(){null!==$content_form&&$content_form.trigger("clean-smiform",{form:$forms,container:$content
 _form}),smi.shortcuts.remove("form"),$content.undelegate(".form-section","focusin"),$content.undelegate(".form-section","click"),$content.removeClass("form-content"),$content.empty()}}}})})(
 jQuery,infrae);
\ No newline at end of file
+(function($,infrae){var scroll_field_into_view=function($content,$field){var $section=$field.closest(".form-section");if($section.length){var section_top=$section.position().top,section_height=$section.outerHeight(),section_bottom=section_top+section_height,content_top=$content.scrollTop(),content_height=$content.innerHeight(),content_bottom=content_top+content_height;content_top>section_top-25?infrae.ui.scroll($content,"fast","absolute",section_top-25):section_bottom+25>content_bottom&&infrae.ui.scroll($content,"fast","absolute",section_bottom-content_height+25)}},unfocus_form_fields=function($content){$content.find(".form-section").removeClass("form-focus")},focus_form_field=function($content,$field,no_input_focus){var $section=$field.closest(".form-section");$section.is(".form-focus")|
 |(unfocus_form_fields($content),$section.addClass("form-focus"),no_input_focus||$section.find(".field:first").focus())},focus_first_form_field=function($content){var $field=$content.find(".f
 orm-error:first").find(".field:first");$field.length||($field=$content.find(".field-required:first"),$field.length||($field=$content.find(".field:first"))),$field.length&&(focus_form_field($content,$field),scroll_field_into_view($content,$field))},focus_next_form_field=function($content){var $next_body,$next_form,$focused=$content.find(".form-focus"),$field=$focused.next();$field.length||($next_body=$focused.closest(".form-body").nextAll(".form-body"),$next_body.length?$field=$next_body.find(".form-section:first"):($next_form=$focused.closest("form").nextAll("form"),$field=$next_form.length?$next_form.find(".form-section:first"):$content.find(".form-section:first"))),focus_form_field($content,$field),scroll_field_into_view($content,$field)},focus_previous_form_field=function($content){var
  $prev_body,$prev_form,$focused=$content.find(".form-focus"),$field=$focused.prev();$field.length||($prev_body=$focused.closest(".form-body").prevAll(".form-body"),$prev_body.length?$field=$
 prev_body.find(".form-section:last"):($prev_form=$focused.closest("form").prevAll("form"),$field=$prev_form.length?$prev_form.find(".form-section:last"):$content.find(".form-section:last"))),focus_form_field($content,$field),scroll_field_into_view($content,$field)};$(document).on("load-smiform",".form-content",function(event,data){var $container=data.container||$(this),$content=data.$content||$container,$form=data.form||$container.find("form"),focus_form_event=function(event){var $target=$(event.target),$section=$target.closest(".form-section"),is_cancelable=$target.is("input")||$target.is("a");focus_form_field($content,$section,is_cancelable),is_cancelable||event.stopPropagation()};$container.delegate(".form-section","click",focus_form_event),$container.delegate(".form-section","focusin"
 ,focus_form_event),$container.delegate(".form-error-detail","click",function(){$(this).fadeOut().promise().done(function(){$(this).remove()})}),focus_first_form_field($content),$form.trigger
 ("loadwidget-smiform",data)}),infrae.views.view({iface:"form",name:"content",factory:function($content,data,smi){var $container=null,$form=$([]),objection=null;return{template_data:!0,render:function(){if(data.portlets){var $portlets=$('<div class="portlets form-content"></div>');$container=$('<div class="forms form-content"></div>'),$portlets.html(data.portlets),$content.append($container),$content.append($portlets)}else $container=$content,$container.addClass("form-content");$container.html(data.forms),smi.shortcuts.create("form",$container,!0),$form=$container.find("form"),$form.each(function(){var $form=$(this),form_prefix=$form.attr("name"),submit=function($control){return function(){var queue=$.Deferred(),submission=queue,have_control=void 0!==$control&&$control.length,message=void 
 0,is_link=!1,serialize=function(){var values;return $form.trigger("serialize-smiform",{form:$form,container:$container}),values=$form.serializeArray(),have_control&&values.push({name:$contro
 l.attr("name"),value:$control.text()}),values};if(have_control&&(message=$control.attr("data-confirmation"),is_link=$control.hasClass("link-control")),!is_link){if(void 0!==message){var title=$control.attr("data-confirmation-title");title||(title="Please confirm"),queue=queue.then(function(){return infrae.ui.ConfirmationDialog({title:title,message:message})})}return queue.then(function(){if(null!==objection){var result=objection();if(result)return result.done(function(){objection=null})}return $.Deferred().resolve()},function(){return $.Deferred().reject()}).then(function(){return smi.ajax.send_to_opened(serialize())}),submission.resolve(),!1}$control.attr("href",[smi.get_screen_url(),"?",$.param(serialize())].join(""))}};$form.bind("submit",submit($form.find(".form-controls a.default-for
 m-control"))),$form.find(".form-controls a.form-control").each(function(){var $control=$(this),shortcut=$control.attr("data-form-shortcut"),handler=submit($control);$control.bind("click",han
 dler),shortcut&&smi.shortcuts.bind("form",null,[shortcut],handler)}),$form.find(".form-controls a.form-button").each(function(){var $control=$(this),shortcut=$control.attr("data-form-shortcut");shortcut&&smi.shortcuts.bind("form",null,[shortcut],function(){return $control.click(),!1})}),$form.delegate(".form-controls a.form-popup","click",function(){var $control=$(this),handler=submit();return $control.SMIFormPopup().done(function(data){data.extra&&data.extra.refresh==form_prefix&&handler()}),!1}),$form.tipsy({delegate:"a.form-control"}),$form.tipsy({delegate:"a.form-button"}),$form.attr("data-form-url",smi.get_screen_url())}),$container.trigger("load-smiform",{form:$form,container:$container,$content:$content,set_objection:function(deferred){objection=deferred}}),smi.shortcuts.bind("form
 ",null,["ctrl+down","ctrl+shift+down"],function(){return focus_next_form_field($content),!1}),smi.shortcuts.bind("form",null,["ctrl+up","ctrl+shift+up"],function(){return focus_previous_form
 _field($content),!1})},cleanup:function(){null!==$container&&$container.trigger("clean-smiform",{form:$form,container:$container}),smi.shortcuts.remove("form"),$content.undelegate(".form-section","focusin"),$content.undelegate(".form-section","click"),$content.removeClass("form-content"),$content.empty()}}}})})(jQuery,infrae);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.js
--- a/src/silva/ui/static/js/smi.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.js	Thu Sep 26 14:33:42 2013 +0200
@@ -3,8 +3,9 @@
     var HASH_REGEXP = /#([^!]*)!?(.*)/;
 
     /**
-     * Screen location. This permit you define the default screen
-       location, test for a location, or open a location.
+     * Screen location, i.e. content path and opened screen. This
+     * permit you define the default screen location, test if you are
+     * already at given a location or open a location.
      */
     var Screen = function(default_screen) {
         var api = {
@@ -130,6 +131,8 @@
             },
             /**
              * Return a given screen URL.
+             *
+             * @param screen: optional screen to compute the URL.
              */
             get_screen_url: function(screen) {
                 if (!screen) {
@@ -192,12 +195,13 @@
                                 // the callback up until the javascript are loaded.
                                 if (payload.resources.js) {
                                     delayed = delayed.then(function() {
-                                        return $.when.apply(
-                                            $, infrae.utils.map(payload.resources.js, resources.load_js));
+                                        return resources.load_js(payload.resources.js);
                                     });
                                 };
                                 if (payload.resources.css) {
-                                    infrae.utils.each(payload.resources.css, resources.load_css);
+                                    delayed = delayed.then(function() {
+                                        return resources.load_css(payload.resources.css);
+                                    });
                                 };
                             };
                             for (name in plugins) {
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.min.js
--- a/src/silva/ui/static/js/smi.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,infrae,jsontemplate){var HASH_REGEXP=/#([^!]*)!?(.*)/,Screen=function(default_screen){var api={ifaces:["redirect"],path:"",screen:"",default_screen:function(){var current_default=Screen(default_screen);return current_default.path=api.path,current_default.screen=default_screen,current_default},is_default_screen:function(){return api.screen===default_screen},copy:function(other){api.path=other.path,api.screen=other.screen},equal:function(other){return api.path==other.path&&api.screen==other.screen},open:function(new_path,new_screen){new_path||(new_path=api.path),new_screen||(new_screen=api.screen),document.location.hash=new_screen+"!"+new_path},read:function(hash){var parts=HASH_REGEXP.exec(hash);parts?(api.screen=parts[1],api.path=parts[2]):(api.screen="",api.path=""),api.scree
 n.length||(api.screen=default_screen)}};return api};$.fn.SMI=function(options){var smi={objection:null,ready:infrae.deferred.SemaphoreCallbacks(),options:options,opened:Screen("content"),ope
 ning:Screen("content"),shortcuts:infrae.keyboard.ShortcutManager(document)},$workspace=$(options.workspace.selector),$workspace_header=$workspace.children(".header"),$workspace_content=$workspace.children(".content");infrae.ui.selection.disable($workspace_header.children(".metadata")),options.theme&&options.theme.background&&$("html").css("background-color",options.theme.background);var name,screen_url=new jsontemplate.Template(options.screen,{}),action_url=new jsontemplate.Template(options.action,{}),content_url=new jsontemplate.Template(options.workspace.url,{}),plugins={},resources=infrae.views.HTMLResourceManager($(document)),default_error_handlers={};for(name in infrae.smi.plugins)plugins[name]=infrae.smi.plugins[name](smi,options);infrae.views.view({iface:"screen",factory:function($
 content,data,smi){return{render:function(){$workspace_content.render({data:data.screen,name:"content",args:[smi]}).done(function(){void 0!==data.metadata&&$workspace_header.render({data:data
 .metadata,name:"header",args:[smi,content_url,this]})})}}}}),$.extend(smi,{clipboard:infrae.smi.ClipBoardManager(plugins.notifications),get_language:function(){var lang=$("html").attr("lang");return lang||(lang="en"),lang},get_screen_url:function(screen){return screen||(screen=smi.opened),screen_url.expand(screen)},open_screen:function(path,screen){smi.opened.open(path,screen)},open_screen_from_link:function(link){var path=link.attr("href");path&&"#"!=path||(path=smi.opened.path),smi.open_screen(path,link.attr("rel"))}}),$(document).delegate("a.open-screen","click",function(){return smi.open_screen_from_link($(this)),!1}),$.extend(smi,{ajax:{query:function(url,data,error_handlers){var query={};return query.url=url,query.dataType="json",data&&(query.type="POST",query.data=data),$.ajax(quer
 y).then(function(payload){var name,delayed=$.Deferred().resolve(),result=$.Deferred();void 0!=payload.resources&&(payload.resources.js&&(delayed=delayed.then(function(){return $.when.apply($
 ,infrae.utils.map(payload.resources.js,resources.load_js))})),payload.resources.css&&infrae.utils.each(payload.resources.css,resources.load_css));for(name in plugins)void 0!==plugins[name].page&&plugins[name].page(payload);return delayed.done(function(){result.resolve(payload.content)}),result},function(request){if(!smi.options.testing){void 0===error_handlers&&(error_handlers=default_error_handlers);var handler=error_handlers[request.status];void 0===handler&&(handler=error_handlers[500]);var result_handler=handler(request);if(result_handler)return void 0!==result_handler.promise?result_handler:$(document).render({data:result_handler,args:[smi],reject:[request]})}return $.Deferred().reject(request)})},vote:function(callback){if(!smi.objection)return callback();var deferred=smi.objection(
 );return deferred||(deferred=$.Deferred(),deferred.resolve()),deferred.done(function(){smi.objection=null}),deferred.then(callback,function(){return $.Deferred(function(deferred){deferred.re
 ject({status:417})})})},lock:function(callback){return smi.ready.use(),callback().then(function(){return smi.ready.release(200),{}},function(request){return smi.ready.release(request.status),{}})},create_error_handlers:function(custom_handlers,parent_handlers){var default_handler,code,handlers={};void 0===parent_handlers&&(parent_handlers=default_error_handlers);for(code in parent_handlers)handlers[code]=parent_handlers[code];for(code in custom_handlers)default_handler=parent_handlers[code],void 0===default_handler&&(default_handler=parent_handlers[500]),handlers[code]=custom_handlers[code](default_handler);return handlers},send_to_opened:function(data,error_handlers){return smi.ajax.lock(function(){return smi.ajax.vote(function(){return smi.ajax.query(smi.get_screen_url(smi.opening),data
 ,error_handlers).then(function(payload){return smi.opened.copy(smi.opening),$(document).render({data:payload,args:[smi]})})}).then(null,function(request){return smi.opening.equal(smi.opened)
 ||(smi.opening.copy(smi.opened),smi.opening.open()),request})})}},open_action_from_link:function(link){return smi.ajax.lock(function(){return smi.ajax.vote(function(){var action=link.attr("rel"),path=link.attr("href");return path||(path=smi.opened.path),smi.ajax.query(action_url.expand({path:path,action:action}),{path:smi.opened.path,screen:smi.opened.screen}).then(function(payload){return $(document).render({data:payload,args:[smi]})})})})}}),default_error_handlers=function(){var ajax_handler=function(default_handler){return function(request){try{var name,payload=$.parseJSON(request.responseText);for(name in plugins)void 0!==plugins[name].error&&plugins[name].error(payload);return payload.content}catch(e){return default_handler(request)}}};return smi.ajax.create_error_handlers({400:ajax_
 handler,401:ajax_handler},options.error_messages)}(),$(document).delegate("a.open-action","click",function(){return smi.open_action_from_link($(this)),!1}),$(document).delegate("a.text-overl
 ay","click",function(){return smi.ajax.query($(this).attr("href")).then(function(payload){return $(document).render({data:payload,args:[smi]})}),!1}),$(document).trigger("load-smiplugins",smi);var read_hash=function(hash){var error_handlers=smi.ajax.create_error_handlers({404:function(default_handler){return function(request){return smi.opening.is_default_screen()?default_handler(request):$.Deferred().resolve(smi.opening.default_screen())}}});return read_hash=function(hash){smi.opening.read(hash),smi.opening.equal(smi.opened)||smi.ajax.send_to_opened(void 0,error_handlers)},read_hash(hash)};return $(window).hashchange(function(event,data){read_hash(data.after)}),read_hash(document.location.hash),smi},infrae.smi={plugins:{}}})(jQuery,infrae,jsontemplate);
\ No newline at end of file
+(function($,infrae,jsontemplate){var HASH_REGEXP=/#([^!]*)!?(.*)/,Screen=function(default_screen){var api={ifaces:["redirect"],path:"",screen:"",default_screen:function(){var current_default=Screen(default_screen);return current_default.path=api.path,current_default.screen=default_screen,current_default},is_default_screen:function(){return api.screen===default_screen},copy:function(other){api.path=other.path,api.screen=other.screen},equal:function(other){return api.path==other.path&&api.screen==other.screen},open:function(new_path,new_screen){new_path||(new_path=api.path),new_screen||(new_screen=api.screen),document.location.hash=new_screen+"!"+new_path},read:function(hash){var parts=HASH_REGEXP.exec(hash);parts?(api.screen=parts[1],api.path=parts[2]):(api.screen="",api.path=""),api.scree
 n.length||(api.screen=default_screen)}};return api};$.fn.SMI=function(options){var smi={objection:null,ready:infrae.deferred.SemaphoreCallbacks(),options:options,opened:Screen("content"),ope
 ning:Screen("content"),shortcuts:infrae.keyboard.ShortcutManager(document)},$workspace=$(options.workspace.selector),$workspace_header=$workspace.children(".header"),$workspace_content=$workspace.children(".content");infrae.ui.selection.disable($workspace_header.children(".metadata")),options.theme&&options.theme.background&&$("html").css("background-color",options.theme.background);var name,screen_url=new jsontemplate.Template(options.screen,{}),action_url=new jsontemplate.Template(options.action,{}),content_url=new jsontemplate.Template(options.workspace.url,{}),plugins={},resources=infrae.views.HTMLResourceManager($(document)),default_error_handlers={};for(name in infrae.smi.plugins)plugins[name]=infrae.smi.plugins[name](smi,options);infrae.views.view({iface:"screen",factory:function($
 content,data,smi){return{render:function(){$workspace_content.render({data:data.screen,name:"content",args:[smi]}).done(function(){void 0!==data.metadata&&$workspace_header.render({data:data
 .metadata,name:"header",args:[smi,content_url,this]})})}}}}),$.extend(smi,{clipboard:infrae.smi.ClipBoardManager(plugins.notifications),get_language:function(){var lang=$("html").attr("lang");return lang||(lang="en"),lang},get_screen_url:function(screen){return screen||(screen=smi.opened),screen_url.expand(screen)},open_screen:function(path,screen){smi.opened.open(path,screen)},open_screen_from_link:function(link){var path=link.attr("href");path&&"#"!=path||(path=smi.opened.path),smi.open_screen(path,link.attr("rel"))}}),$(document).delegate("a.open-screen","click",function(){return smi.open_screen_from_link($(this)),!1}),$.extend(smi,{ajax:{query:function(url,data,error_handlers){var query={};return query.url=url,query.dataType="json",data&&(query.type="POST",query.data=data),$.ajax(quer
 y).then(function(payload){var name,delayed=$.Deferred().resolve(),result=$.Deferred();void 0!=payload.resources&&(payload.resources.js&&(delayed=delayed.then(function(){return resources.load
 _js(payload.resources.js)})),payload.resources.css&&(delayed=delayed.then(function(){return resources.load_css(payload.resources.css)})));for(name in plugins)void 0!==plugins[name].page&&plugins[name].page(payload);return delayed.done(function(){result.resolve(payload.content)}),result},function(request){if(!smi.options.testing){void 0===error_handlers&&(error_handlers=default_error_handlers);var handler=error_handlers[request.status];void 0===handler&&(handler=error_handlers[500]);var result_handler=handler(request);if(result_handler)return void 0!==result_handler.promise?result_handler:$(document).render({data:result_handler,args:[smi],reject:[request]})}return $.Deferred().reject(request)})},vote:function(callback){if(!smi.objection)return callback();var deferred=smi.objection();return
  deferred||(deferred=$.Deferred(),deferred.resolve()),deferred.done(function(){smi.objection=null}),deferred.then(callback,function(){return $.Deferred(function(deferred){deferred.reject({st
 atus:417})})})},lock:function(callback){return smi.ready.use(),callback().then(function(){return smi.ready.release(200),{}},function(request){return smi.ready.release(request.status),{}})},create_error_handlers:function(custom_handlers,parent_handlers){var default_handler,code,handlers={};void 0===parent_handlers&&(parent_handlers=default_error_handlers);for(code in parent_handlers)handlers[code]=parent_handlers[code];for(code in custom_handlers)default_handler=parent_handlers[code],void 0===default_handler&&(default_handler=parent_handlers[500]),handlers[code]=custom_handlers[code](default_handler);return handlers},send_to_opened:function(data,error_handlers){return smi.ajax.lock(function(){return smi.ajax.vote(function(){return smi.ajax.query(smi.get_screen_url(smi.opening),data,error_h
 andlers).then(function(payload){return smi.opened.copy(smi.opening),$(document).render({data:payload,args:[smi]})})}).then(null,function(request){return smi.opening.equal(smi.opened)||(smi.o
 pening.copy(smi.opened),smi.opening.open()),request})})}},open_action_from_link:function(link){return smi.ajax.lock(function(){return smi.ajax.vote(function(){var action=link.attr("rel"),path=link.attr("href");return path||(path=smi.opened.path),smi.ajax.query(action_url.expand({path:path,action:action}),{path:smi.opened.path,screen:smi.opened.screen}).then(function(payload){return $(document).render({data:payload,args:[smi]})})})})}}),default_error_handlers=function(){var ajax_handler=function(default_handler){return function(request){try{var name,payload=$.parseJSON(request.responseText);for(name in plugins)void 0!==plugins[name].error&&plugins[name].error(payload);return payload.content}catch(e){return default_handler(request)}}};return smi.ajax.create_error_handlers({400:ajax_handler,
 401:ajax_handler},options.error_messages)}(),$(document).delegate("a.open-action","click",function(){return smi.open_action_from_link($(this)),!1}),$(document).delegate("a.text-overlay","cli
 ck",function(){return smi.ajax.query($(this).attr("href")).then(function(payload){return $(document).render({data:payload,args:[smi]})}),!1}),$(document).trigger("load-smiplugins",smi);var read_hash=function(hash){var error_handlers=smi.ajax.create_error_handlers({404:function(default_handler){return function(request){return smi.opening.is_default_screen()?default_handler(request):$.Deferred().resolve(smi.opening.default_screen())}}});return read_hash=function(hash){smi.opening.read(hash),smi.opening.equal(smi.opened)||smi.ajax.send_to_opened(void 0,error_handlers)},read_hash(hash)};return $(window).hashchange(function(event,data){read_hash(data.after)}),read_hash(document.location.hash),smi},infrae.smi={plugins:{}}})(jQuery,infrae,jsontemplate);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.notification.js
--- a/src/silva/ui/static/js/smi.notification.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.notification.js	Thu Sep 26 14:33:42 2013 +0200
@@ -9,6 +9,7 @@
         var $container = $(options.notifications.selector),
             defaults = {
                 themeState: 'feedback',
+                life: options.notifications.life,
                 speed: options.notifications.speed === undefined && 'normal' || options.notifications.speed};
 
         // Default jGrowl notifications
@@ -62,17 +63,40 @@
 
         };
 
-        // Support for webkit notificatons.
-        if (!options.notifications.standard && window.webkitNotifications) {
-            var native_api = window.webkitNotifications;
+        // Support for notifications.
+        // Safari has the webkitNotifications object defined as Chrome has.
+        if (!options.notifications.standard && window.webkitNotifications ) {
+            // Check if user is using Chrome browser.
+            var using_chrome = (function() {
+                if (navigator.userAgent.indexOf("Chrome") > -1) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+            })();
 
+            var native_api;
+            if (using_chrome) {
+                // WebKit API
+                native_api = window.webkitNotifications;
+            }
+            else {
+                // W3C-Safari API.
+                native_api = window.Notification;
+            }
             var is_api_enabled = function(now) {
-                var status = native_api.checkPermission();
-
-                if (status == 0) {
+                var status;
+                if (using_chrome) {
+                    status = native_api.checkPermission();
+                }
+                else {
+                    status = native_api.permission;
+                }
+                if (status == 0 || status === 'granted') {
                     // This is authorized
                     return true;
-                } else if (status == 1) {
+                } else if (status == 1 || status === 'default') {
                     try {
                         // Hopyfully we will get it. Safari requires a callback.
                         native_api.requestPermission(function(){});
@@ -89,14 +113,27 @@
                 $.extend(api, {
                     notify: function(message) {
                         if (is_api_enabled(false)) {
-                            var notification = native_api.createNotification('', message.message, '');
-                            if (message.autoclose) {
-                                notification.ondisplay = function () {
+                            var notification;
+                            if (using_chrome) {
+                                notification = native_api.createNotification('', message.message, '');
+                            }
+                            else {
+                                notification = new Notification('', {'body': message.message});
+                            }
+                            if (options.notifications.life) {
+                                var close_after = function () {
                                     setTimeout(function() {
                                         notification.cancel();
-                                    }, message.autoclose);
+                                    }, options.notifications.life);
                                 };
-                            };
+                                if (using_chrome) {
+                                    notification.ondisplay = close_after;
+                                }
+                                else {
+                                    notification.onshow = close_after;
+                                }
+                             }
+
                             notification.show();
                         } else {
                             default_notify(message);
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.notification.min.js
--- a/src/silva/ui/static/js/smi.notification.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.notification.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,infrae){var NotificationManager=function(smi,options){var defaults=($(options.notifications.selector),{themeState:"feedback",speed:void 0===options.notifications.speed&&"normal"||options.notifications.speed}),default_notify=function(message){var options=$.extend({},defaults);"error"==message.category&&(options.themeState="error"),$.jGrowl(message.message,options)};$(document).on("click","#jGrowl",function(event){$(this).jGrowl("close"),event.stopPropagation(),event.preventDefault()});var api={notify:default_notify,notifies:function(messages){infrae.utils.each(messages,api.notify)},page:function(data){return void 0!=data.notifications&&infrae.utils.each(data.notifications,api.notify),null},error:function(data){return void 0!=data.notifications&&infrae.utils.each(data.notificati
 ons,api.notify),null}};if(!options.notifications.standard&&window.webkitNotifications){var native_api=window.webkitNotifications,is_api_enabled=function(now){var status=native_api.checkPermi
 ssion();if(0==status)return!0;if(1==status)try{return native_api.requestPermission(function(){}),now===!0}catch(error){return!1}return!1};is_api_enabled(!0)&&$.extend(api,{notify:function(message){if(is_api_enabled(!1)){var notification=native_api.createNotification("",message.message,"");message.autoclose&&(notification.ondisplay=function(){setTimeout(function(){notification.cancel()},message.autoclose)}),notification.show()}else default_notify(message)}})}return $(document).bind("refresh-feedback-smi",function(){$.getJSON(options.notifications.url,function(messages){messages.notifications&&api.notifies(messages.notifications)})}),$(document).bind("notify-feedback-smi",function(event,data){"array"==typeof data?api.notifies(data):api.notify(data)}),api};$.extend(infrae.smi.plugins,{notifi
 cations:NotificationManager})})(jQuery,infrae);
\ No newline at end of file
+(function($,infrae){var NotificationManager=function(smi,options){var defaults=($(options.notifications.selector),{themeState:"feedback",life:options.notifications.life,speed:void 0===options.notifications.speed&&"normal"||options.notifications.speed}),default_notify=function(message){var options=$.extend({},defaults);"error"==message.category&&(options.themeState="error"),$.jGrowl(message.message,options)};$(document).on("click","#jGrowl",function(event){$(this).jGrowl("close"),event.stopPropagation(),event.preventDefault()});var api={notify:default_notify,notifies:function(messages){infrae.utils.each(messages,api.notify)},page:function(data){return void 0!=data.notifications&&infrae.utils.each(data.notifications,api.notify),null},error:function(data){return void 0!=data.notifications&&i
 nfrae.utils.each(data.notifications,api.notify),null}};if(!options.notifications.standard&&window.webkitNotifications){var native_api,using_chrome=function(){return navigator.userAgent.index
 Of("Chrome")>-1?!0:!1}();native_api=using_chrome?window.webkitNotifications:window.Notification;var is_api_enabled=function(now){var status;if(status=using_chrome?native_api.checkPermission():native_api.permission,0==status||"granted"===status)return!0;if(1==status||"default"===status)try{return native_api.requestPermission(function(){}),now===!0}catch(error){return!1}return!1};is_api_enabled(!0)&&$.extend(api,{notify:function(message){if(is_api_enabled(!1)){var notification;if(notification=using_chrome?native_api.createNotification("",message.message,""):new Notification("",{body:message.message}),options.notifications.life){var close_after=function(){setTimeout(function(){notification.cancel()},options.notifications.life)};using_chrome?notification.ondisplay=close_after:notification.ons
 how=close_after}notification.show()}else default_notify(message)}})}return $(document).bind("refresh-feedback-smi",function(){$.getJSON(options.notifications.url,function(messages){messages.
 notifications&&api.notifies(messages.notifications)})}),$(document).bind("notify-feedback-smi",function(event,data){"array"==typeof data?api.notifies(data):api.notify(data)}),api};$.extend(infrae.smi.plugins,{notifications:NotificationManager})})(jQuery,infrae);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.workspace.js
--- a/src/silva/ui/static/js/smi.workspace.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.workspace.js	Thu Sep 26 14:33:42 2013 +0200
@@ -158,8 +158,9 @@
                         name = smi.options.theme.name;
                     };
                     infrae.ui.icon($content.children('ins'), data.icon);
-                    // XXX Need a check for IE.
-                    $('head > title').html(name + " - " + data.title);
+                    if (!$.browser.msie) {
+                        $('head > title').html(name + " - " + data.title);
+                    };
                 }
             };
         }
@@ -378,8 +379,10 @@
 
                     // Update parent link
                     if (data.up != null) {
-                        $parent.attr('href', data.up ||'/');
-                        $parent.attr('rel', smi.opened.tab);
+                        if (data.up.path) {
+                            $parent.attr('href', data.up.path);
+                        };
+                        $parent.attr('rel', data.up.screen || smi.opened.tab);
                         $parent.removeClass('ui-state-disabled');
                         $parent.addClass('ui-state-default');
                     } else {
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/static/js/smi.workspace.min.js
--- a/src/silva/ui/static/js/smi.workspace.min.js	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/static/js/smi.workspace.min.js	Thu Sep 26 14:33:42 2013 +0200
@@ -1,1 +1,1 @@
-(function($,jsontemplate,infrae){infrae.views.view({iface:"preview",name:"content",factory:function($content){var resolution_fixed=!1,resolution_height=0,resolution_width=0,max_height=0,max_width=0,resize=function($iframe){$iframe.height(Math.min(max_height,resolution_height)),$iframe.width(Math.min(max_width,resolution_width))};return{template_data:!0,template_nocache:!0,iframe:!0,resize_iframe:function(width,height){return max_width=width,max_height=height,resolution_fixed?(resize(this.$iframe),!0):!1},set_resolution:function(width,height){resolution_fixed=!0,resolution_height=height,resolution_width=width,resize(this.$iframe)},clear_resolution:function(){resolution_fixed=!1,this.$iframe.trigger("resize")},cleanup:function(){$content.empty()}}}}),infrae.views.view({iface:"preview",name:
 "toolbar",factory:function($content,data,smi,view){var actions=data.menu.actions,resolutions=smi.options.preview?smi.options.preview.resolutions:[],have_actions=actions&&actions.entries.leng
 th;return{jsont:'{.section have_actions}<div class="actions content-toolbar"><ol></ol></div>{.end}{.section resolutions}<div class="view-toolbar resolution-preview"><select>{.repeated section @}<option value={resolution|htmltag}>{name}</option>{.end}</select></div>{.end}',have_actions:have_actions,resolutions:resolutions,render:function(){have_actions&&$content.find(".content-toolbar ol").render({data:actions}),resolutions.length&&$content.find(".resolution-preview select").on("change",function(){var value=$(this).attr("value"),resolution=value.match(/(\d*)x(\d*)/);resolution?view.set_resolution(resolution[1],resolution[2]):view.clear_resolution()})},cleanup:function(){$content.empty()}}}}),infrae.views.view({iface:"redirect",factory:function($content,data,smi){return{render:function(){sm
 i.open_screen(data.path,data.screen)}}}});var text_template=new jsontemplate.Template('<div id="smi-text"><div class="smi-text-content">{html}</div></div>',{});infrae.views.view({iface:"text
 -overlay",factory:function($content,data){var $text=$(text_template.expand(data)),$overlay=$('<div class="ui-widget-overlay"></div>'),clear=function(event){return $(event.target).is("a")?void 0:($text.remove(),$overlay.remove(),!1)};return{render:function(){$overlay.click(clear),$text.click(clear),$("body").append($overlay),$("body").append($text)}}}}),infrae.views.view({iface:"view",factory:function($content,data){return{render:function(){window.open(data.url)}}}}),infrae.views.view({iface:"message",factory:function($content,data){return{render:function(){return infrae.ui.ConfirmationDialog({title:data.title,message:data.message,not_cancellable:!0})}}}}),infrae.views.view({iface:"title",factory:function($content,data,smi){return{jsont:'<ins class="icon"></ins>{data.title|html}',render:fu
 nction(){var name="Silva";smi.options.theme&&smi.options.theme.name&&(name=smi.options.theme.name),infrae.ui.icon($content.children("ins"),data.icon),$("head > title").html(name+" - "+data.t
 itle)}}}}),infrae.views.view({iface:"menu",factory:function($content,data){var tabs=$content.hasClass("tabs"),$opened=$([]),create=function(info,top_level){var $tab=$("<li><a></a></li>"),$link=$tab.children("a");if(tabs&&(top_level?(info.screen?$link.addClass("top-screen"):$link.addClass("top-label"),$link.addClass("top-entry"),$tab.addClass("top-level")):$tab.addClass("sub-level")),info.name?$link.html("<span>"+info.name+"</span>"):info.logo&&$link.html('<ins class="tab-logo '+info.logo+'"/>'),info.screen?($link.addClass("open-screen"),$link.attr("rel",info.screen)):info.action?($link.addClass("open-action"),$link.attr("rel",info.action)):info.url&&($link.attr("href",info.url),info.target&&$link.attr("target",info.target)),info.icon){var icon_class="tab-icon";info.action&&(icon_class="ac
 tion-icon"),$link.addClass("ui-state-default"),$link.prepend('<div class="'+icon_class+'"><ins class="ui-icon ui-icon-'+info.icon+'"></ins></div>')}if(info.active&&$link.addClass("active"),i
 nfo.description&&$link.attr("title",info.description),info.accesskey&&$link.attr("accesskey",info.accesskey),info.trigger&&$link.addClass(info.trigger),info.entries){var $container=$('<ol class="subtabs"></ol>'),$opener=$('<div class="subtab-icon"><ins></ins></div>'),$trigger=info.screen&&top_level?$opener:$link,close=function(exclude){var contained=[],uncontained=[],container=$container.get(0);$opened.each(function(){this===container&&!exclude||$.contains(this,container)?contained.push(this):uncontained.push(this)}),$(uncontained).fadeOut("fast"),$opened=$(contained),$opened=exclude?$(contained):$(contained).add(container)};$.each(info.entries,function(i,entry){$container.append(create(entry))}),$container.bind("mouseleave",function(){close(!0)}),$container.bind("click",function(){close(
 !0)}),$trigger.bind(top_level?"click":"mouseenter",function(){var origin=$link.offset().left,width=$container.width(),available=$(document).width();if(top_level)available>origin+width?$conta
 iner.css("left",0):$container.css("right",0);else{var padding=$link.width();available>origin+padding+width?$container.css("left",padding):$container.css("right",width-5),$container.css("top",0)}return close(!1),$container.show(),!1}),$link.prepend($opener),$tab.append($container)}return $tab};return{render:function(){$.each(data.entries,function(i,info){$content.append(create(info,!0))}),$content.tipsy({delegate:"a"}),infrae.ui.selection.disable($content),tabs||$content.children("li:last").addClass("last-action")},cleanup:function(){$content.unbind("open-menu"),$content.unbind(".tipsy"),$content.empty()}}}}),infrae.views.view({iface:"object",name:"header",factory:function($content,data,smi,url,view){var make_menu=function($menu,data){data?(($menu.is("ol")?$menu:$menu.find("ol")).render({d
 ata:data}),$menu.show()):$menu.hide()},make_all_menu=function($metadata,data,compact){if(make_menu($metadata.children(".user-menu"),data.user),compact){var entries=[];data.content&&(entries=
 entries.concat(data.content.entries)),data.view&&(entries=entries.concat(data.view.entries)),make_menu($metadata.find(".view-actions"),void 0),make_menu($metadata.children(".content-tabs"),void 0),make_menu($metadata.find(".compact-tabs"),{ifaces:["menu"],entries:[{logo:"tab-options",entries:entries}]})}else make_menu($metadata.find(".view-actions"),data.view),make_menu($metadata.children(".content-tabs"),data.content),make_menu($metadata.find(".compact-tabs"),void 0)};return{render:function(){var $metadata=$content.children(".metadata"),$parent=$content.find("a.parent"),compact=$content.hasClass("compact-header");$metadata.children("h2").render({data:data.title,args:[smi,view]}),$metadata.children("#content-url").attr("href",url.expand({path:data.path})),make_all_menu($metadata,data.menu
 ,compact),$content.find(".admin-actions").tipsy({delegate:"a"}),$content.children(".toolbar").render({data:data,name:"toolbar",args:[smi,view]}),null!=data.up?($parent.attr("href",data.up||"
 /"),$parent.attr("rel",smi.opened.tab),$parent.removeClass("ui-state-disabled"),$parent.addClass("ui-state-default")):($parent.addClass("ui-state-disabled"),$parent.removeClass("ui-state-default")),$(window).bind("fullscreen-resize-smi.default-header",function(event,info){make_all_menu($metadata,data.menu,info.active)})},cleanup:function(){$content.find(".admin-actions").unbind(".tipsy"),$(window).unbind("fullscreen-resize-smi.default-header")}}}}),infrae.views.view({iface:"object",name:"toolbar",factory:function($content,data){var actions=data.menu.actions,have_actions=actions&&actions.entries.length;return{jsont:'{.section have_actions}<div class="actions content-toolbar"><ol></ol></div>{.end}',have_actions:have_actions,render:function(){have_actions&&$content.find(".content-toolbar ol"
 ).render({data:actions})},cleanup:function(){$content.empty()}}}})})(jQuery,jsontemplate,infrae);
\ No newline at end of file
+(function($,jsontemplate,infrae){infrae.views.view({iface:"preview",name:"content",factory:function($content){var resolution_fixed=!1,resolution_height=0,resolution_width=0,max_height=0,max_width=0,resize=function($iframe){$iframe.height(Math.min(max_height,resolution_height)),$iframe.width(Math.min(max_width,resolution_width))};return{template_data:!0,template_nocache:!0,iframe:!0,resize_iframe:function(width,height){return max_width=width,max_height=height,resolution_fixed?(resize(this.$iframe),!0):!1},set_resolution:function(width,height){resolution_fixed=!0,resolution_height=height,resolution_width=width,resize(this.$iframe)},clear_resolution:function(){resolution_fixed=!1,this.$iframe.trigger("resize")},cleanup:function(){$content.empty()}}}}),infrae.views.view({iface:"preview",name:
 "toolbar",factory:function($content,data,smi,view){var actions=data.menu.actions,resolutions=smi.options.preview?smi.options.preview.resolutions:[],have_actions=actions&&actions.entries.leng
 th;return{jsont:'{.section have_actions}<div class="actions content-toolbar"><ol></ol></div>{.end}{.section resolutions}<div class="view-toolbar resolution-preview"><select>{.repeated section @}<option value={resolution|htmltag}>{name}</option>{.end}</select></div>{.end}',have_actions:have_actions,resolutions:resolutions,render:function(){have_actions&&$content.find(".content-toolbar ol").render({data:actions}),resolutions.length&&$content.find(".resolution-preview select").on("change",function(){var value=$(this).attr("value"),resolution=value.match(/(\d*)x(\d*)/);resolution?view.set_resolution(resolution[1],resolution[2]):view.clear_resolution()})},cleanup:function(){$content.empty()}}}}),infrae.views.view({iface:"redirect",factory:function($content,data,smi){return{render:function(){sm
 i.open_screen(data.path,data.screen)}}}});var text_template=new jsontemplate.Template('<div id="smi-text"><div class="smi-text-content">{html}</div></div>',{});infrae.views.view({iface:"text
 -overlay",factory:function($content,data){var $text=$(text_template.expand(data)),$overlay=$('<div class="ui-widget-overlay"></div>'),clear=function(event){return $(event.target).is("a")?void 0:($text.remove(),$overlay.remove(),!1)};return{render:function(){$overlay.click(clear),$text.click(clear),$("body").append($overlay),$("body").append($text)}}}}),infrae.views.view({iface:"view",factory:function($content,data){return{render:function(){window.open(data.url)}}}}),infrae.views.view({iface:"message",factory:function($content,data){return{render:function(){return infrae.ui.ConfirmationDialog({title:data.title,message:data.message,not_cancellable:!0})}}}}),infrae.views.view({iface:"title",factory:function($content,data,smi){return{jsont:'<ins class="icon"></ins>{data.title|html}',render:fu
 nction(){var name="Silva";smi.options.theme&&smi.options.theme.name&&(name=smi.options.theme.name),infrae.ui.icon($content.children("ins"),data.icon),$.browser.msie||$("head > title").html(n
 ame+" - "+data.title)}}}}),infrae.views.view({iface:"menu",factory:function($content,data){var tabs=$content.hasClass("tabs"),$opened=$([]),create=function(info,top_level){var $tab=$("<li><a></a></li>"),$link=$tab.children("a");if(tabs&&(top_level?(info.screen?$link.addClass("top-screen"):$link.addClass("top-label"),$link.addClass("top-entry"),$tab.addClass("top-level")):$tab.addClass("sub-level")),info.name?$link.html("<span>"+info.name+"</span>"):info.logo&&$link.html('<ins class="tab-logo '+info.logo+'"/>'),info.screen?($link.addClass("open-screen"),$link.attr("rel",info.screen)):info.action?($link.addClass("open-action"),$link.attr("rel",info.action)):info.url&&($link.attr("href",info.url),info.target&&$link.attr("target",info.target)),info.icon){var icon_class="tab-icon";info.action&
 &(icon_class="action-icon"),$link.addClass("ui-state-default"),$link.prepend('<div class="'+icon_class+'"><ins class="ui-icon ui-icon-'+info.icon+'"></ins></div>')}if(info.active&&$link.addC
 lass("active"),info.description&&$link.attr("title",info.description),info.accesskey&&$link.attr("accesskey",info.accesskey),info.trigger&&$link.addClass(info.trigger),info.entries){var $container=$('<ol class="subtabs"></ol>'),$opener=$('<div class="subtab-icon"><ins></ins></div>'),$trigger=info.screen&&top_level?$opener:$link,close=function(exclude){var contained=[],uncontained=[],container=$container.get(0);$opened.each(function(){this===container&&!exclude||$.contains(this,container)?contained.push(this):uncontained.push(this)}),$(uncontained).fadeOut("fast"),$opened=$(contained),$opened=exclude?$(contained):$(contained).add(container)};$.each(info.entries,function(i,entry){$container.append(create(entry))}),$container.bind("mouseleave",function(){close(!0)}),$container.bind("click",f
 unction(){close(!0)}),$trigger.bind(top_level?"click":"mouseenter",function(){var origin=$link.offset().left,width=$container.width(),available=$(document).width();if(top_level)available>ori
 gin+width?$container.css("left",0):$container.css("right",0);else{var padding=$link.width();available>origin+padding+width?$container.css("left",padding):$container.css("right",width-5),$container.css("top",0)}return close(!1),$container.show(),!1}),$link.prepend($opener),$tab.append($container)}return $tab};return{render:function(){$.each(data.entries,function(i,info){$content.append(create(info,!0))}),$content.tipsy({delegate:"a"}),infrae.ui.selection.disable($content),tabs||$content.children("li:last").addClass("last-action")},cleanup:function(){$content.unbind("open-menu"),$content.unbind(".tipsy"),$content.empty()}}}}),infrae.views.view({iface:"object",name:"header",factory:function($content,data,smi,url,view){var make_menu=function($menu,data){data?(($menu.is("ol")?$menu:$menu.find(
 "ol")).render({data:data}),$menu.show()):$menu.hide()},make_all_menu=function($metadata,data,compact){if(make_menu($metadata.children(".user-menu"),data.user),compact){var entries=[];data.co
 ntent&&(entries=entries.concat(data.content.entries)),data.view&&(entries=entries.concat(data.view.entries)),make_menu($metadata.find(".view-actions"),void 0),make_menu($metadata.children(".content-tabs"),void 0),make_menu($metadata.find(".compact-tabs"),{ifaces:["menu"],entries:[{logo:"tab-options",entries:entries}]})}else make_menu($metadata.find(".view-actions"),data.view),make_menu($metadata.children(".content-tabs"),data.content),make_menu($metadata.find(".compact-tabs"),void 0)};return{render:function(){var $metadata=$content.children(".metadata"),$parent=$content.find("a.parent"),compact=$content.hasClass("compact-header");$metadata.children("h2").render({data:data.title,args:[smi,view]}),$metadata.children("#content-url").attr("href",url.expand({path:data.path})),make_all_menu($me
 tadata,data.menu,compact),$content.find(".admin-actions").tipsy({delegate:"a"}),$content.children(".toolbar").render({data:data,name:"toolbar",args:[smi,view]}),null!=data.up?(data.up.path&&
 $parent.attr("href",data.up.path),$parent.attr("rel",data.up.screen||smi.opened.tab),$parent.removeClass("ui-state-disabled"),$parent.addClass("ui-state-default")):($parent.addClass("ui-state-disabled"),$parent.removeClass("ui-state-default")),$(window).bind("fullscreen-resize-smi.default-header",function(event,info){make_all_menu($metadata,data.menu,info.active)})},cleanup:function(){$content.find(".admin-actions").unbind(".tipsy"),$(window).unbind("fullscreen-resize-smi.default-header")}}}}),infrae.views.view({iface:"object",name:"toolbar",factory:function($content,data){var actions=data.menu.actions,have_actions=actions&&actions.entries.length;return{jsont:'{.section have_actions}<div class="actions content-toolbar"><ol></ol></div>{.end}',have_actions:have_actions,render:function(){hav
 e_actions&&$content.find(".content-toolbar ol").render({data:actions})},cleanup:function(){$content.empty()}}}})})(jQuery,jsontemplate,infrae);
\ No newline at end of file
diff -r c437ae72d668 -r cd75455e3296 src/silva/ui/tests/test_icon.py
--- a/src/silva/ui/tests/test_icon.py	Sun Aug 25 17:51:53 2013 +0200
+++ b/src/silva/ui/tests/test_icon.py	Thu Sep 26 14:33:42 2013 +0200
@@ -20,6 +20,30 @@
         self.root = self.layer.get_application()
         self.layer.login('author')
 
+    def test_icon_resolver(self):
+        """Test adapter to retrieve icon urls'.
+        """
+        request = TestRequest(layers=[ISilvaUITheme])
+        resolver = queryAdapter(request, IIconResolver)
+        self.assertTrue(verifyObject(IIconResolver, resolver))
+
+        # Class are return instead of URLs for a direct access to the sprite.
+        self.assertEqual(
+            resolver.get_content_url(self.root),
+            'silva_root')
+        self.assertEqual(
+            resolver.get_content_url(None),
+            'http://localhost/root/++static++/silva.icons/missing.png')
+        self.assertEqual(
+            resolver.get_identifier_url('Silva Root'),
+            'silva_root')
+        self.assertEqual(
+            resolver.get_identifier_url(None),
+            'http://localhost/root/++static++/silva.icons/missing.png')
+        self.assertEqual(
+            resolver.get_identifier_url('best content in the world'),
+            'http://localhost/root/++static++/silva.icons/generic.png')
+
     def test_icon_tag(self):
         """SMI skin define a custom policy for icon.
         """
@@ -38,7 +62,7 @@
             '<ins class="icon silva_root"></ins>')
         self.assertEqual(
             resolver.get_tag(identifier='default'),
-            '<img height="16" width="16" src="http://localhost/root/++static++/silva.icons/silvageneric.gif" alt="default" />')
+            '<img height="16" width="16" src="http://localhost/root/++static++/silva.icons/generic.png" alt="default" />')
         self.assertEqual(
             resolver.get_tag(),
             '<img height="16" width="16" src="http://localhost/root/++static++/silva.icons/missing.png" alt="Missing" />')