diff --git a/bowerstatic/core.py b/bowerstatic/core.py index 520da94..4732730 100644 --- a/bowerstatic/core.py +++ b/bowerstatic/core.py @@ -18,18 +18,19 @@ def __init__(self, publisher_signature='bowerstatic', autoversion=None): self._renderer = Renderer() self.autoversion = autoversion or filesystem_second_autoversion - def components(self, name, path): + def components(self, name, path, bottom=False): if name in self._component_collections: raise Error("Duplicate name for components directory: %s" % name) - result = ComponentCollection(self, name, path=path) + result = ComponentCollection(self, name, path=path, bottom=bottom) self._component_collections[name] = result return result - def local_components(self, name, component_collection): + def local_components(self, name, component_collection, bottom=False): if name in self._component_collections: raise Error("Duplicate name for local components: %s" % name) result = ComponentCollection(self, name, - fallback_collection=component_collection) + fallback_collection=component_collection, + bottom=bottom) self._component_collections[name] = result return result @@ -63,12 +64,13 @@ def get_filename(self, bower_components_name, class ComponentCollection(object): - def __init__(self, bower, name, path=None, fallback_collection=None): + def __init__(self, bower, name, path=None, fallback_collection=None, bottom=False): self.bower = bower self.name = name self._resources = {} self.path = path self.fallback_collection = fallback_collection + self.bottom = bottom if path is not None: self._components = self.load_components(path) else: @@ -267,6 +269,10 @@ def url(self): def renderer(self, resource): return self.bower.renderer(resource) + @property + def bottom(self): + return self.component_collection.bottom + class Resource(object): def __init__(self, component, file_path, dependencies): @@ -290,3 +296,7 @@ def content(self): def renderer(self): return self.component.renderer(self) + + @property + def bottom(self): + return self.file_path.endswith('.js') and self.component.bottom diff --git a/bowerstatic/includer.py b/bowerstatic/includer.py index 57f82cb..a807ff4 100644 --- a/bowerstatic/includer.py +++ b/bowerstatic/includer.py @@ -31,10 +31,13 @@ def __init__(self): def add(self, inclusion): self._inclusions.append(inclusion) - def render(self): + def render(self, bottom=False): inclusions = topological_sort( self._inclusions, lambda inclusion: inclusion.dependencies()) - snippets = [inclusion.html() for inclusion in inclusions] + snippets = [inclusion.html() for inclusion in inclusions + if (not bottom and not inclusion.bottom) + or (bottom and inclusion.bottom) + ] return '\n'.join(snippets) @@ -71,3 +74,7 @@ def dependencies(self): def html(self): return self.renderer(self.resource) + + @property + def bottom(self): + return self.resource.bottom diff --git a/bowerstatic/injector.py b/bowerstatic/injector.py index 61d029b..22d4b01 100644 --- a/bowerstatic/injector.py +++ b/bowerstatic/injector.py @@ -22,7 +22,9 @@ def __call__(self, request): body = response.body response.body = b'' body = body.replace( - b'', b''.join((inclusions.render().encode(), b''))) + b'', b''.join((inclusions.render(bottom=False).encode(), b''))) + body = body.replace( + b'', b''.join((inclusions.render(bottom=True).encode(), b''))) response.write(body) return response diff --git a/bowerstatic/tests/test_injector.py b/bowerstatic/tests/test_injector.py index 6415f64..a3065c2 100644 --- a/bowerstatic/tests/test_injector.py +++ b/bowerstatic/tests/test_injector.py @@ -542,6 +542,41 @@ def wsgi(environ, start_response): b'Hello!') +def test_injector_bottom(): + bower = bowerstatic.Bower() + + components = bower.components('components', os.path.join( + os.path.dirname(__file__), 'bower_components'), bottom=True) + + def wsgi(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/html;charset=UTF-8')]) + include = components.includer(environ) + # going to pull in jquery-ui and jquery twice + include('jquery-ui') + include('jquery-ui-bootstrap') + return [b'Hello!'] + + injector = bower.injector(wsgi) + + c = Client(injector) + + response = c.get('/') + + assert response.body == ( + b'' + b'' + b'Hello!' + b'\n' + b'' + b'' + ) + def test_injector_no_head_to_inject(): bower = bowerstatic.Bower() diff --git a/doc/integrating.rst b/doc/integrating.rst index b3cb93e..538a316 100644 --- a/doc/integrating.rst +++ b/doc/integrating.rst @@ -211,6 +211,21 @@ or:: include('static/favicon.ico', lambda resource: '') + +Including at the bottom +----------------------- + +For javascripts, it will sometimes be better to include the script at the bottom. To include all `*.js` files +of a component at the bottom, you have to add `bottom=True` when loading components:: + + components = bower.components('components', '/path/to/bower_components', + bottom=True) + +Local components do have the same parameter:: + + local = bower.local_components('local', components, bottom=True) + + URL structure -------------