Anonymous avatar Anonymous committed 981b625 Merge

Sync up to default.

Comments (0)

Files changed (9)

 .installed.cfg
 *.egg-info
 *.pyc
+*.swp
 *~
 develop-eggs
 dist

File contents unchanged.

doc/development.rst

 
 .. _pyflakes: http://divmod.org/trac/wiki/DivmodPyflakes
 
-
-.. _buildbot:
-
-Buildbot
---------
-
-The fanstatic tests are run daily on the `THA buildbot <http://dev.thehealthagency.com/buildbot/>`_.
-We are working on a pretty overview of the buildbot status.
-For now, just search for ``fanstatic`` in the `list of projects <http://dev.thehealthagency.com/buildbot/one_box_per_builder>`_.
-
-The configuration of the buildbot lives on
-svn+ssh://svn.zope.org/repos/main/Sandbox/janjaapdriessen/buildbot
-
 Building the documentation
 --------------------------
 

doc/libraries.rst

       - **library**
       - **source**
 
+    * - :pypi:`js.ace`
+      - `Ajax.org Cloud9 Editor <https://github.com/ajaxorg/ace>`_
+      - `bitbucket <https://bitbucket.org/fanstatic/js.ace>`__
+
+    * - :pypi:`js.amcharts`
+      - `amCharts <http://www.amcharts.com>`_
+      - `Github <https://github.com/securactive/js.amcharts>`__
+
+    * - :pypi:`js.bootstrap`
+      - `Bootstrap, from Twitter <http://twitter.github.com/bootstrap/index.html>`_
+      - `github <https://github.com/RedTurtle/js.bootstrap>`__
+
+    * - :pypi:`js.chosen`
+      - `Chosen <http://harvesthq.github.com/chosen/>`_
+      - ?
+
+    * - :pypi:`js.ckeditor`
+      - `CKEditor <http://ckeditor.com/>`_
+      - ?
+
+    * - :pypi:`js.classy`
+      - `Classy - Classes for JavaScript <http://classy.pocoo.org/>`_
+      - `bitbucket <https://bitbucket.org/fanstatic/js.classy>`__
+
     * - :pypi:`js.extjs`
       - _`ExtJS`: http://www.sencha.com/products/js/
       - `bitbucket <http://bitbucket.org/fanstatic/js.extjs>`__
       - `Galleriffic <http://www.twospy.com/galleriffic>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.yui>`__
 
-    * - :pypi:`js.jquery`
-      - `jQuery <http://jquery.com>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery>`__
+    * - :pypi:`js.jquery_datalink`
+      - the jQuery plugin `Datalink <https://github.com/nje/jquery-datalink>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_datalink>`__
+
+    * - :pypi:`js.jquery_datatables`
+      - the jQuery plugin `DataTable <http://www.datatables.net>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_datatables>`__
+
+    * - :pypi:`js.jquery_expandbox`
+      - `jquery.expandBox <http://projects.stephane-klein.info/jquery.expandBox/>`_
+      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_expandbox>`__
+
+    * - :pypi:`js.jquery_form`
+      - the jQuery plugin `Form <http://jquery.malsup.com/form>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_form>`__
 
     * - :pypi:`js.jquery_jgrowl`
       - `jGrowl <http://stanlemon.net/projects/jgrowl.html>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_jgrowl>`__
 
+    * - :pypi:`js.jquery_jqote2`
+      - `jquery.jqote2 <https://github.com/aefxx/jQote2>`_
+      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_jqote2>`__
+
+    * - :pypi:`js.jquery_jstree`
+      - the jQuery plugin `JsTree <http://www.jstree.com/>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_jstree>`__
+
     * - :pypi:`js.jquery_metadata`
       - `jQuery Metadata <http://plugins.jquery.com/project/metadata>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_metadata>`__
 
+    * - :pypi:`js.jquery_qtip`
+      - `jquery.qTip <http://craigsworks.com/projects/qtip/>`_
+      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_qtip>`__
+
+    * - :pypi:`js.jquery_qunit`
+      - the jQuery plugin `QUnit <http://docs.jquery.com/Qunit>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_qunit>`__
+
     * - :pypi:`js.jquery_slimbox`
       - the jQuery plugin `Slimbox <http://www.digitalia.be/software/slimbox2>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_slimbox>`__
       - the jQuery plugin `Tooltip <http://bassistance.de/jquery-plugins/jquery-plugin-tooltip>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_tooltip>`__
 
-    * - :pypi:`js.jqueryui`
-      - `jQuery UI <http://jqueryui.com>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jqueryui>`__
-
-    * - :pypi:`js.tinymce`
-      - `TinyMCE <http://tinymce.moxiecode.com>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.tinymce>`__
-
-    * - :pypi:`js.yui`
-      - the `YUI Library <http://developer.yahoo.com/yui>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.yui>`__
-
-    * - :pypi:`js.jquery_datalink`
-      - the jQuery plugin `Datalink <https://github.com/nje/jquery-datalink>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_datalink>`__
-
-    * - :pypi:`js.jquery_datatables`
-      - the jQuery plugin `DataTable <http://www.datatables.net>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_datatables>`__
-
-    * - :pypi:`js.jquery_form`
-      - the jQuery plugin `Form <http://jquery.malsup.com/form>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_form>`__
-
-    * - :pypi:`js.jquery_jstree`
-      - the jQuery plugin `JsTree <http://www.jstree.com/>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_jstree>`__
-
-    * - :pypi:`js.jquery_qunit`
-      - the jQuery plugin `QUnit <http://docs.jquery.com/Qunit>`_
-      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_qunit>`__
-
     * - :pypi:`js.jquery_utils`
       - `jQuery Utils <http://code.google.com/p/jquery-utils/>`_
       - `bitbucket <http://bitbucket.org/fanstatic/js.jquery_utils>`__
 
-    * - :pypi:`js.classy`
-      - `Classy - Classes for JavaScript <http://classy.pocoo.org/>`_
-      - `bitbucket <https://bitbucket.org/fanstatic/js.classy>`__
+    * - :pypi:`js.jquery`
+      - `jQuery <http://jquery.com>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jquery>`__
 
-    * - :pypi:`js.jquery_expandbox`
-      - `jquery.expandBox <http://projects.stephane-klein.info/jquery.expandBox/>`_
-      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_expandbox>`__
-
-    * - :pypi:`js.jquery_jqote2`
-      - `jquery.jqote2 <https://github.com/aefxx/jQote2>`_
-      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_jqote2>`__
-
-    * - :pypi:`js.jquery_qtip`
-      - `jquery.qTip <http://craigsworks.com/projects/qtip/>`_
-      - `bitbucket <https://bitbucket.org/fanstatic/js.jquery_qtip>`__
+    * - :pypi:`js.jqueryui`
+      - `jQuery UI <http://jqueryui.com>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.jqueryui>`__
 
     * - :pypi:`js.lesscss`
       - `less.js <http://lesscss.org/>`_
       - `bitbucket <https://bitbucket.org/fanstatic/js.lesscss>`__
 
-    * - :pypi:`js.bootstrap`
-      - `Bootstrap, from Twitter <http://twitter.github.com/bootstrap/index.html>`_
-      - `github <https://github.com/RedTurtle/js.bootstrap>`__
-
-    * - :pypi:`js.chosen`
-      - `Chosen <http://harvesthq.github.com/chosen/>`_
-      - ?
+    * - :pypi:`js.lightbox`
+      - `jquery lightbox <http://leandrovieira.com/projects/jquery/lightbox/>`_
+      - `github <https://github.com/amleczko/js.lightbox>`__
 
     * - :pypi:`js.modernizr`
       - `Modernizr <http://modernizr.com/>`_
       - ?
 
+    * - :pypi:`js.raphael`
+      - `Raphael <http://raphaeljs.com/>`_
+      - ?
+
     * - :pypi:`js.spin`
       - `spin.js <http://fgnass.github.com/spin.js/>`_
       - ?
 
-    * - :pypi:`js.raphael`
-      - `Raphael <http://raphaeljs.com/>`_
-      - ?
+    * - :pypi:`js.sugar`
+      - `Sugar <http://sugarjs.com/>`_
+      - `github <https://github.com/disko/js.sugar>`__
+
+    * - :pypi:`js.tinymce`
+      - `TinyMCE <http://tinymce.moxiecode.com>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.tinymce>`__
 
     * - :pypi:`js.underscore`
       - `underscore.js <http://documentcloud.github.com/underscore/>`_
       - ?
 
-    * - :pypi:`js.sugar`
-      - `Sugar <http://sugarjs.com/>`_
-      - `github <https://github.com/disko/js.sugar>`__
-
-    * - :pypi:`js.lightbox`
-      - `jquery lightbox <http://leandrovieira.com/projects/jquery/lightbox/>`_
-      - `github <https://github.com/amleczko/js.lightbox>`__
-
-    * - :pypi:`js.ace`
-      - `Ajax.org Cloud9 Editor <https://github.com/ajaxorg/ace>`_
-      - `bitbucket <https://bitbucket.org/fanstatic/js.ace>`__
-
-    * - :pypi:`js.ckeditor`
-      - `CKEditor <http://ckeditor.com/>`_
-      - ?
-
+    * - :pypi:`js.yui`
+      - the `YUI Library <http://developer.yahoo.com/yui>`_
+      - `bitbucket <http://bitbucket.org/fanstatic/js.yui>`__
 
 Follow the instructions in the :doc:`development section <development>` to learn how to package your own library.

fanstatic/core.py

 import os
 import sys
+import re
 import threading
 
 import fanstatic.checksum
 DEBUG = 'debug'
 MINIFIED = 'minified'
 
+_head_regex = re.compile('(<head[^>]*>)')
 
 _resource_file_existence_checking = True
 
     If a slot is required, it must be filled in by passing an extra
     dictionary parameter to the ``.need`` method, containing a mapping
     from the required :py:class:`Slot` to :py:class:`Resource`.
-    
+
     When a slot is filled, the resource filled in should have
     the same dependencies as the slot, or a subset of the dependencies
     of the slot. It should also have the same extension as the slot.
     If this is not the case, it is an error.
     """
-    
+
 class Library(object):
     """The resource library.
 
         self._library_deps = set()
         self.known_resources = {}
         self.library_nr = None
-        
+
     def __repr__(self):
         return "<Library '%s' at '%s'>" % (self.name, self.path)
 
                     'Library cycle detected in resource %s' % resource)
 
     def register(self, resource):
-        """Register a Resource with this Library. 
+        """Register a Resource with this Library.
 
         A Resource knows about its Library. After a Resource has registered
-        itself with its Library, the Library knows about the Resources 
-        associated to it. 
+        itself with its Library, the Library knows about the Resources
+        associated to it.
         """
         if resource.relpath in self.known_resources:
             raise ConfigurationError(
 
     :param depends: optionally, a list of resources that this resource
       depends on. Entries in the list are :py:class:`Resource`
-      instances. 
+      instances.
 
     :param supersedes: optionally, a list of :py:class:`Resource`
       instances that this resource supersedes as a rollup
             elif isinstance(argument, basestring):
                 mode_resource = Resource(library, argument, bottom=bottom)
             else:
-                # The dependencies of a mode resource should be the same 
+                # The dependencies of a mode resource should be the same
                 # or a subset of the dependencies this mode replaces.
                 if len(argument.depends - self.depends) > 0:
                     raise ModeResourceDependencyError
             dependency_nr = max(depend.dependency_nr + 1,
                                 dependency_nr)
         self.dependency_nr = dependency_nr
-   
+
     def render(self, library_url):
         return self.renderer('%s/%s' % (library_url, self.relpath))
 
     real resource by the application when you ``.need()`` that
     resource (or when you need something that depends on the slot
     indirectly).
-  
+
     :param library: the :py:class:`Library` this slot is in.
 
     :param ext: the extension of the slot, for instance '.js'. This
       required to be filled in when a resource that depends on a slot
       is needed, or whether it's optional. By default filling in a
       slot is required.
-      
+
     :param depends: optionally, a list of resources that this slot
       depends on. Resources that are slotted in here need to have
       the same dependencies as that of the slot, or a strict subset.
         assert extension.startswith('.')
         self.ext = extension
         self.required = required
-        
+
         assert not isinstance(depends, basestring)
         self.depends = set()
         if depends is not None:
             dependency_nr = max(depend.dependency_nr + 1,
                                 dependency_nr)
         self.dependency_nr = dependency_nr
-        
+
 class FilledSlot(Renderable, Dependable):
     def __init__(self, slot, resource):
         self.library = resource.library
             raise SlotError(
                 "slot filled in with resource that has dependencies that "
                 "are not a strict subset of dependencies of slot")
-        
+
         # XXX how do slots interact with rollups?
 
     def render(self, library_url):
         except KeyError:
             # fall back on the default mode if mode not found
             return self
-        
+
 class Group(Dependable):
     """A resource used to group resources together.
 
         slots = slots or {}
         self._resources.add(resource)
         self._slots.update(slots)
-        
+
     def resources(self):
         """Retrieve the list of resources needed.
 
                                 resource)
             result.add(FilledSlot(resource, fill_resource))
         return result
-            
+
     def clear(self):
         # Clear out any resources "needed" thusfar.
         # XXX or should we rather revert to the list with resources
           inclusions into. This string must have a ``<head>`` section.
         """
         to_insert = self.render()
-        return html.replace('<head>', '<head>\n    %s\n' % to_insert, 1)
+        return _head_regex.sub('\\1\n    %s\n' % (to_insert,), html, count=1)
 
     def render_topbottom(self):
         """Render resource inclusions separately into top and bottom fragments.
         """
         top, bottom = self.render_topbottom()
         if top:
-            html = html.replace('<head>', '<head>\n    %s\n' % top, 1)
+            html = _head_regex.sub('\\1\n    %s\n' % (top,), html, count=1)
         if bottom:
             html = html.replace('</body>', '%s</body>' % bottom, 1)
         return html
             # nothing to supersede resource so use it directly
             result.append(resource)
     return result
-    
+
 def sort_resources(resources):
     """Sort resources for inclusion on web page.
 
     """
     for resource in resources:
         resource.library.init_library_nr()
-        
+
     def key(resource):
         return (
             resource.order,

fanstatic/injector.py

 from fanstatic.config import convert_config
 import fanstatic
 
+CONTENT_TYPES = ['text/html', 'text/xml', 'application/xhtml+xml']
 
 class Injector(object):
     """Fanstatic injector WSGI framework component.
 
         # We only continue if the content-type is appropriate.
         if not (response.content_type and
-                response.content_type.lower() in ['text/html', 'text/xml']):
+                response.content_type.lower() in CONTENT_TYPES):
             # Clean up after our behinds.
             fanstatic.del_needed()
             return response

fanstatic/test_core.py

     # Can not use the same relpath for two Resource declarations.
     with pytest.raises(ConfigurationError):
         x2 = Resource(foo, 'a.js')
-    
+
 
 def test_group_resource():
     foo = Library('foo', '')
     c = Resource(foo, 'c.js', depends=[g])
     g2 = Group([g])
     g3 = Group([g, g2])
-    
+
     assert c.depends == set([a, b])
     assert g2.depends == set([a, b])
     assert g3.depends == set([a, b])
-    
+
     needed = NeededResources()
     needed.need(c)
     assert needed.resources() == [a, b, c]
-    
+
 def test_redundant_resource():
     foo = Library('foo', '')
     x1 = Resource(foo, 'a.js')
     needed = NeededResources(resources=[h1, h2], rollup=True, debug=True)
     # no mode available for rollup, use the rollup.
     assert needed.resources() == [gianth]
-     
+
 
 def test_rendering():
     foo = Library('foo', '')
 something more</head></html>'''
 
 
+def test_html_insert_head_with_attributes():
+    # ticket 72: .need() broken when <head> tag has attributes
+    foo = Library('foo', '')
+    x1 = Resource(foo, 'a.js')
+    needed = NeededResources(resources=[x1])
+
+    html = '<html><head profile="http://example.org">something</head></html>'
+    assert needed.render_into_html(html) == '''\
+<html><head profile="http://example.org">
+    <script type="text/javascript" src="/fanstatic/foo/a.js"></script>
+something</head></html>'''
+
 def test_html_top_bottom():
     foo = Library('foo', '')
     x1 = Resource(foo, 'a.js')
     X.init_library_nr()
     Y.init_library_nr()
     Z.init_library_nr()
-                              
+
     assert a.library.library_nr == 0
     assert c.library.library_nr == 0
     assert b.library.library_nr == 1
     lib2 = Library('lib2', '')
     lib3 = Library('lib3', '')
     lib4 = Library('lib4', '')
-    
+
     js1 = Resource(lib1, 'js1.js')
-    js2 = Resource(lib2, 'js2.js', depends=[js1]) 
+    js2 = Resource(lib2, 'js2.js', depends=[js1])
     js3 = Resource(lib3, 'js3.js', depends=[js2])
-    
+
     style1 = Resource(lib3, 'style1.css')
     style2 = Resource(lib4, 'style2.css', depends=[style1])
 
     obviel_lib = Library('obviel', '')
     bread_lib = Library('bread', '')
     app_lib = Library('app', '')
-    
+
     jquery = Resource(jquery_lib, 'jquery.js')
     jqueryui = Resource(jqueryui_lib, 'jqueryui.js', depends=[jquery])
 
     vtab = Resource(bread_lib, 'vtab.js', depends=[jqueryui])
 
     tabview = Resource(bread_lib, 'tabview.js', depends=[obviel, vtab])
-    
+
     bread = Resource(bread_lib, 'bread.js', depends=[tabview, obviel_forms])
 
     app = Resource(app_lib, 'app.js', depends=[bread, obviel_datepicker])
-    
+
     needed = NeededResources()
 
     needed.need(app)
         print resource, resource.library.library_nr
     assert resources == [jquery, jqueryui, obviel, obviel_forms,
                          obviel_datepicker, vtab, tabview, bread, app]
-    
+
 
     #assert resources == [obviel, forms, forms_autocomplete, tabview, bread,
     #                     zorgdas]
-    
+
 # XXX tests for hashed resources when this is enabled. Needs some plausible
 # directory to test for hashes
 

fanstatic/test_injector.py

     wrapped_app = Injector(html_app)
     request = webob.Request.blank('/')
     request.get_response(wrapped_app)
-    # There's no NeededResources objecy anymore after the request has
+    # There's no NeededResources object anymore after the request has
     # been done.
     dummy = get_needed()
     with pytest.raises(NotImplementedError):
     wrapped_app = Injector(textplain_app)
     request = webob.Request.blank('/')
     request.get_response(wrapped_app)
-    # There's no NeededResources objecy anymore after the request has
+    # There's no NeededResources object anymore after the request has
     # been done, even for response content types that would not have
     # been processed by fanstatic's inclusion rendering.
     dummy = get_needed()

fanstatic/test_wsgi.py

     response = request.get_response(wrapped)
 
 def test_serf():
+    pytest.importorskip('mypackage')
     # also test serf config
     d = {
         'resource': 'py:mypackage.style'
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.