235 # content-types (xml/html) and all possible browsers is very |
235 # content-types (xml/html) and all possible browsers is very |
236 # tricky, see http://www.hixie.ch/advocacy/xhtml for an in-depth discussion |
236 # tricky, see http://www.hixie.ch/advocacy/xhtml for an in-depth discussion |
237 xhtml_safe_script_opening = u'<script type="text/javascript"><!--//--><![CDATA[//><!--\n' |
237 xhtml_safe_script_opening = u'<script type="text/javascript"><!--//--><![CDATA[//><!--\n' |
238 xhtml_safe_script_closing = u'\n//--><!]]></script>' |
238 xhtml_safe_script_closing = u'\n//--><!]]></script>' |
239 |
239 |
240 def __init__(self): |
240 def __init__(self, datadir_url=None): |
241 super(HTMLHead, self).__init__() |
241 super(HTMLHead, self).__init__() |
242 self.jsvars = [] |
242 self.jsvars = [] |
243 self.jsfiles = [] |
243 self.jsfiles = [] |
244 self.cssfiles = [] |
244 self.cssfiles = [] |
245 self.ie_cssfiles = [] |
245 self.ie_cssfiles = [] |
246 self.post_inlined_scripts = [] |
246 self.post_inlined_scripts = [] |
247 self.pagedata_unload = False |
247 self.pagedata_unload = False |
|
248 self.datadir_url = datadir_url |
248 |
249 |
249 |
250 |
250 def add_raw(self, rawheader): |
251 def add_raw(self, rawheader): |
251 self.write(rawheader) |
252 self.write(rawheader) |
252 |
253 |
279 :param jsfile: the script's URL |
280 :param jsfile: the script's URL |
280 """ |
281 """ |
281 if jsfile not in self.jsfiles: |
282 if jsfile not in self.jsfiles: |
282 self.jsfiles.append(jsfile) |
283 self.jsfiles.append(jsfile) |
283 |
284 |
284 def add_css(self, cssfile, media): |
285 def add_css(self, cssfile, media='all'): |
285 """adds `cssfile` to the list of javascripts used in the webpage |
286 """adds `cssfile` to the list of javascripts used in the webpage |
286 |
287 |
287 This function checks if the file has already been added |
288 This function checks if the file has already been added |
288 :param cssfile: the stylesheet's URL |
289 :param cssfile: the stylesheet's URL |
289 """ |
290 """ |
298 def add_unload_pagedata(self): |
299 def add_unload_pagedata(self): |
299 """registers onunload callback to clean page data on server""" |
300 """registers onunload callback to clean page data on server""" |
300 if not self.pagedata_unload: |
301 if not self.pagedata_unload: |
301 self.post_inlined_scripts.append(self.js_unload_code) |
302 self.post_inlined_scripts.append(self.js_unload_code) |
302 self.pagedata_unload = True |
303 self.pagedata_unload = True |
|
304 |
|
305 def concat_urls(self, urls): |
|
306 """concatenates urls into one url usable by Apache mod_concat |
|
307 |
|
308 This method returns the url without modifying it if there is only |
|
309 one element in the list |
|
310 :param urls: list of local urls/filenames to concatenate |
|
311 """ |
|
312 if len(urls) == 1: |
|
313 return urls[0] |
|
314 len_prefix = len(self.datadir_url) |
|
315 concated = u','.join(url[len_prefix:] for url in urls) |
|
316 return (u'%s??%s' % (self.datadir_url, concated)) |
|
317 |
|
318 def group_urls(self, urls_spec): |
|
319 """parses urls_spec in order to generate concatenated urls |
|
320 for js and css includes |
|
321 |
|
322 This method checks if the file is local and if it shares options |
|
323 with direct neighbors |
|
324 :param urls_spec: entire list of urls/filenames to inspect |
|
325 """ |
|
326 concatable = [] |
|
327 prev_islocal = False |
|
328 prev_key = None |
|
329 for url, key in urls_spec: |
|
330 islocal = url.startswith(self.datadir_url) |
|
331 if concatable and (islocal != prev_islocal or key != prev_key): |
|
332 yield (self.concat_urls(concatable), prev_key) |
|
333 del concatable[:] |
|
334 if not islocal: |
|
335 yield (url, key) |
|
336 else: |
|
337 concatable.append(url) |
|
338 prev_islocal = islocal |
|
339 prev_key = key |
|
340 if concatable: |
|
341 yield (self.concat_urls(concatable), prev_key) |
|
342 |
303 |
343 |
304 def getvalue(self, skiphead=False): |
344 def getvalue(self, skiphead=False): |
305 """reimplement getvalue to provide a consistent (and somewhat browser |
345 """reimplement getvalue to provide a consistent (and somewhat browser |
306 optimzed cf. http://stevesouders.com/cuzillion) order in external |
346 optimzed cf. http://stevesouders.com/cuzillion) order in external |
307 resources declaration |
347 resources declaration |
316 vardecl = (u'if (typeof %s == "undefined") {%s}' % |
356 vardecl = (u'if (typeof %s == "undefined") {%s}' % |
317 (var, vardecl)) |
357 (var, vardecl)) |
318 w(vardecl + u'\n') |
358 w(vardecl + u'\n') |
319 w(self.xhtml_safe_script_closing) |
359 w(self.xhtml_safe_script_closing) |
320 # 2/ css files |
360 # 2/ css files |
321 for cssfile, media in self.cssfiles: |
361 for cssfile, media in (self.group_urls(self.cssfiles) if self.datadir_url else self.cssfiles): |
322 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' % |
362 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' % |
323 (media, xml_escape(cssfile))) |
363 (media, xml_escape(cssfile))) |
324 # 3/ ie css if necessary |
364 # 3/ ie css if necessary |
325 if self.ie_cssfiles: |
365 if self.ie_cssfiles: |
326 for cssfile, media, iespec in self.ie_cssfiles: |
366 ie_cssfiles = ((x, (y, z)) for x, y, z in self.ie_cssfiles) |
|
367 for cssfile, (media, iespec) in (self.group_urls(ie_cssfiles) if self.datadir_url else ie_cssfiles): |
327 w(u'<!--%s>\n' % iespec) |
368 w(u'<!--%s>\n' % iespec) |
328 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' % |
369 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' % |
329 (media, xml_escape(cssfile))) |
370 (media, xml_escape(cssfile))) |
330 w(u'<![endif]--> \n') |
371 w(u'<![endif]--> \n') |
331 # 4/ js files |
372 # 4/ js files |
332 for jsfile in self.jsfiles: |
373 jsfiles = ((x, None) for x in self.jsfiles) |
|
374 for jsfile, media in self.group_urls(jsfiles) if self.datadir_url else jsfiles: |
333 w(u'<script type="text/javascript" src="%s"></script>\n' % |
375 w(u'<script type="text/javascript" src="%s"></script>\n' % |
334 xml_escape(jsfile)) |
376 xml_escape(jsfile)) |
335 # 5/ post inlined scripts (i.e. scripts depending on other JS files) |
377 # 5/ post inlined scripts (i.e. scripts depending on other JS files) |
336 if self.post_inlined_scripts: |
378 if self.post_inlined_scripts: |
337 w(self.xhtml_safe_script_opening) |
379 w(self.xhtml_safe_script_opening) |