0001"""
0002Asset Tag Helpers
0003
0004Provides functionality for linking an HTML page together with other assets, such as
0005images, javascripts, stylesheets, and feeds.
0006"""
0007# Last synced with Rails copy at Revision 6057 on Feb 7th, 2007.
0008
0009import os
0010import re
0011import urlparse
0012import warnings
0013from tags import *
0014from routes import request_config
0015
0016# The absolute path of the WebHelpers javascripts directory
0017javascript_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
0018 'javascripts')
0019
0020# WebHelpers' built-in javascripts. Note: scriptaculous automatically includes all of its
0021# supporting .js files
0022javascript_builtins = ('prototype.js', 'scriptaculous.js')
0023
0024def auto_discovery_link_tag(source, type='rss', **kwargs):
0025 """
0026 Returns a link tag allowing browsers and news readers (that support it) to auto-detect
0027 an RSS or ATOM feed for current page.
0028
0029 ``source``
0030 The URL of the feed. The URL is ultimately prepended with the environment's
0031 ``SCRIPT_NAME`` (the root path of the web application), unless the URL is
0032 fully-fledged (e.g. http://example.com).
0033
0034 ``type``
0035 The type of feed. Specifying 'rss' or 'atom' automatically translates to a type of
0036 'application/rss+xml' or 'application/atom+xml', respectively. Otherwise the type
0037 is used as specified. Defaults to 'rss'.
0038
0039 Examples::
0040
0041 >>> auto_discovery_link_tag('http://feed.com/feed.xml')
0042 '<link href="http://feed.com/feed.xml" rel="alternate" title="RSS" type="application/rss+xml" />'
0043
0044 >>> auto_discovery_link_tag('http://feed.com/feed.xml', type='atom')
0045 '<link href="http://feed.com/feed.xml" rel="alternate" title="ATOM" type="application/atom+xml" />'
0046
0047 >>> auto_discovery_link_tag('app.rss', type='atom', title='atom feed')
0048 '<link href="app.rss" rel="alternate" title="atom feed" type="application/atom+xml" />'
0049
0050 >>> auto_discovery_link_tag('/app.html', type='text/html')
0051 '<link href="/app.html" rel="alternate" title="" type="text/html" />'
0052 """
0053 title = ''
0054 if type.lower() in ('rss', 'atom'):
0055 title = type.upper()
0056 type = 'application/%s+xml' % type.lower()
0057
0058 tag_args = dict(rel='alternate', type=type, title=title,
0059 href=compute_public_path(source))
0060 kwargs.pop('href', None)
0061 kwargs.pop('type', None)
0062 tag_args.update(kwargs)
0063 return tag('link', **tag_args)
0064
0065def image_tag(source, alt=None, size=None, **options):
0066 """
0067 Returns an image tag for the specified ``source``.
0068
0069 ``source``
0070 The source URL of the image. The URL is prepended with '/images/', unless its full
0071 path is specified. The URL is ultimately prepended with the environment's
0072 ``SCRIPT_NAME`` (the root path of the web application), unless the URL is
0073 fully-fledged (e.g. http://example.com).
0074
0075 ``alt``
0076 The img's alt tag. Defaults to the source's filename, title cased.
0077
0078 ``size``
0079 The img's size, specified in the format "XxY". "30x45" becomes
0080 width="30", height="45". "x20" becomes height="20".
0081
0082 Examples::
0083
0084 >>> image_tag('xml.png')
0085 '<img alt="Xml" src="/images/xml.png" />'
0086
0087 >>> image_tag('rss.png', 'rss syndication')
0088 '<img alt="rss syndication" src="/images/rss.png" />'
0089
0090 >>> image_tag("icon.png", size="16x10", alt="Edit Entry")
0091 '<img alt="Edit Entry" height="10" src="/images/icon.png" width="16" />'
0092
0093 >>> image_tag("/icons/icon.gif", size="16x16")
0094 '<img alt="Icon" height="16" src="/icons/icon.gif" width="16" />'
0095
0096 >>> image_tag("/icons/icon.gif", size="16x")
0097 '<img alt="Icon" src="/icons/icon.gif" width="16" />'
0098 """
0099 if not os.path.splitext(source)[1]:
0100 warnings.warn("You've called image_tag with a source that doesn't include an "
0101 "extension. Soon image_tag will no longer automatically append "
0102 "'.png' to your source. Please call image_path('%s.png') "
0103 "instead." % source, DeprecationWarning, 2)
0104 options['src'] = compute_public_path(source, 'images', 'png')
0105
0106 if not alt:
0107 alt = os.path.splitext(os.path.basename(source))[0].title()
0108 options['alt'] = alt
0109
0110 if size and re.match('^(\d+|)x(\d+|)$', size) and size != 'x':
0111 width, height = size.split('x')
0112 if width:
0113 options['width'] = width
0114 if height:
0115 options['height'] = height
0116
0117 return tag('img', **options)
0118
0119def javascript_include_tag(*sources, **options):
0120 """
0121 Returns script include tags for the specified javascript ``sources``.
0122
0123 Each source's URL path is prepended with '/javascripts/' unless their full path is
0124 specified. Each source's URL path is ultimately prepended with the environment's
0125 ``SCRIPT_NAME`` (the root path of the web application), unless the URL path is a
0126 full-fledged URL (e.g. http://example.com). Sources with no filename extension will be
0127 appended with the '.js' extension.
0128
0129 Optionally includes (prepended) WebHelpers' built-in javascripts when passed the
0130 ``builtins=True`` keyword argument.
0131
0132 Specify the keyword argument ``defer=True`` to enable the script defer attribute.
0133
0134 Examples::
0135
0136 >>> print javascript_include_tag(builtins=True)
0137 <script src="/javascripts/prototype.js" type="text/javascript"></script>
0138 <script src="/javascripts/scriptaculous.js" type="text/javascript"></script>
0139
0140 >>> print javascript_include_tag(builtins=True, defer=True)
0141 <script defer="defer" src="/javascripts/prototype.js" type="text/javascript"></script>
0142 <script defer="defer" src="/javascripts/scriptaculous.js" type="text/javascript"></script>
0143
0144 >>> print javascript_include_tag('prototype', '/other-javascripts/util.js')
0145 <script src="/javascripts/prototype.js" type="text/javascript"></script>
0146 <script src="/other-javascripts/util.js" type="text/javascript"></script>
0147
0148 >>> print javascript_include_tag('app', '/test/test.1.js', builtins=True)
0149 <script src="/javascripts/prototype.js" type="text/javascript"></script>
0150 <script src="/javascripts/scriptaculous.js" type="text/javascript"></script>
0151 <script src="/javascripts/app.js" type="text/javascript"></script>
0152 <script src="/test/test.1.js" type="text/javascript"></script>
0153 """
0154 if options.pop('builtins', False):
0155 sources = javascript_builtins + sources
0156 if options.get('defer') == True:
0157 options['defer'] = 'defer'
0158
0159 tags = []
0160 for source in sources:
0161 content_options = dict(type='text/javascript',
0162 src=compute_public_path(source, 'javascripts',
0163 'js'))
0164 content_options.update(options)
0165 tags.append(content_tag('script', None, **content_options))
0166 return '\n'.join(tags)
0167
0168def stylesheet_link_tag(*sources, **options):
0169 """
0170 Returns CSS link tags for the specified stylesheet ``sources``.
0171
0172 Each source's URL path is prepended with '/stylesheets/' unless their full path is
0173 specified. Each source's URL path is ultimately prepended with the environment's
0174 ``SCRIPT_NAME`` (the root path of the web application), unless the URL path is a
0175 full-fledged URL (e.g. http://example.com). Sources with no filename extension will be
0176 appended with the '.css' extension.
0177
0178 Examples::
0179
0180 >>> stylesheet_link_tag('style')
0181 '<link href="/stylesheets/style.css" media="screen" rel="Stylesheet" type="text/css" />'
0182
0183 >>> stylesheet_link_tag('dir/file', media='all')
0184 '<link href="/stylesheets/dir/file.css" media="all" rel="Stylesheet" type="text/css" />'
0185
0186 >>> stylesheet_link_tag('/dir/file', media='all')
0187 '<link href="/dir/file.css" media="all" rel="Stylesheet" type="text/css" />'
0188 """
0189 tag_options = dict(rel='Stylesheet', type='text/css', media='screen')
0190 tag_options.update(options)
0191 tag_options.pop('href', None)
0192
0193 tags = [tag('link', **dict(href=compute_public_path(source, 'stylesheets', 'css'),
0194 **tag_options)) for source in sources]
0195 return '\n'.join(tags)
0196
0197def compute_public_path(source, root_path=None, ext=None):
0198 """
0199 Format the specified source for publishing, via the public directory, if applicable.
0200 """
0201 if ext and not os.path.splitext(os.path.basename(source))[1]:
0202 source = '%s.%s' % (source, ext)
0203
0204 # Avoid munging fully-fledged URLs, including 'mailto:'
0205 parsed = urlparse.urlparse(source)
0206 if not (parsed[0] and (parsed[1] or parsed[2])):
0207 # Prefix apps deployed under any SCRIPT_NAME path
0208 if not root_path or source.startswith('/'):
0209 source = '%s%s' % (get_script_name(), source)
0210 else:
0211 source = '%s/%s/%s' % (get_script_name(), root_path, source)
0212 return source
0213
0214def get_script_name():
0215 """
0216 Determine the current web application's ``SCRIPT_NAME``.
0217 """
0218 script_name = ''
0219 config = request_config()
0220 if hasattr(config, 'environ'):
0221 script_name = config.environ.get('SCRIPT_NAME', '')
0222 return script_name
0223
0224__all__ = ['javascript_path', 'javascript_builtins', 'auto_discovery_link_tag',
0225 'image_tag', 'javascript_include_tag', 'stylesheet_link_tag']