Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the CGI CPAN distribution.

Report information
The Basics
Id: 38100
Status: rejected
Priority: 0/
Queue: CGI

People
Owner: Nobody in particular
Requestors: ww.galen [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: [PATCH] Added support for conditional comments containing style
Patch is to add preliminary support (& documentation) for conditional comments around style sheets. With the patch, the -style option accepts a -cond argument. For example, print start_html(-style => { -media => 'all', -cond => ['.foo {color: navy;}', '/style/1_IE.css', [5, '/style/IE5.css'], {-src => ['/style/2_IE.css', '/style/3_IE.css'], -code => '.bar {padding: 1em;}'}, ['/style/4.css'], ['!', '/style/notIE.css'], {-expr => '((>=7)|(! 5)&IE', -src => '/style/complex_IE.css', -code => '.foo, .bar { border: #800;}'} ] } ); produces <!--[if IE]> <style type="text/css"> .foo {color: navy;} </style> <link rel="stylesheet" type="text/css" href="/style/1_IE.css" media="all"> <link rel="stylesheet" type="text/css" href="/style/2_IE.css" media="all"> <link rel="stylesheet" type="text/css" href="/style/3_IE.css" media="all"> <style type="text/css"> <!-- .bar {padding: 1em;} --> </style> <![endif]--> <!--[if IE 5]> <link rel="stylesheet" type="text/css" href="/style/IE5.css" media="all"> <![endif]--> <!--[if IE]> <link rel="stylesheet" type="text/css" href="/style/4.css" media="all"> <![endif]--> <![if ! IE]> <link rel="stylesheet" type="text/css" href="/style/notIE.css" > <![endif]> <!--[if ((gte IE 7)|(! IE 5)&IE]> <link rel="stylesheet" type="text/css" href="/style/complex_IE.css" media="all"> <style type="text/css"> <!-- .foo, .bar { border: #800;} --> </style> <![endif]--> Coming eventually: conditional comments for scripts and arbitrary header elements.
Subject: CGI.pm.patch
--- CGI.pm.orig 2008-07-29 11:00:05.000000000 -0400 +++ CGI.pm 2008-08-01 03:49:51.000000000 -0400 @@ -1701,23 +1701,22 @@ #### '_style' => <<'END_OF_FUNC', sub _style { - my ($self,$style) = @_; + my ($self,$style,$cdata_start,$cdata_end) = @_; my (@result); my $type = 'text/css'; my $rel = 'stylesheet'; - - my $cdata_start = $XHTML ? "\n<!--/* <![CDATA[ */" : "\n<!-- "; - my $cdata_end = $XHTML ? "\n/* ]]> */-->\n" : " -->\n"; + $cdata_start ||= $XHTML ? "\n<!--/* <![CDATA[ */" : "\n<!-- "; + $cdata_end ||= $XHTML ? "\n/* ]]> */-->\n" : " -->\n"; my @s = ref($style) eq 'ARRAY' ? @$style : $style; my $other = ''; for my $s (@s) { if (ref($s)) { - my($src,$code,$verbatim,$stype,$alternate,$foo,@other) = - rearrange([qw(SRC CODE VERBATIM TYPE ALTERNATE FOO)], + my($src,$code,$verbatim,$stype,$alternate,$cond,$foo,@other) = + rearrange([qw(SRC CODE VERBATIM TYPE ALTERNATE COND FOO)], ('-foo'=>'bar', ref($s) eq 'ARRAY' ? @$s : %$s)); my $type = defined $stype ? $stype : 'text/css'; @@ -1729,15 +1728,16 @@ foreach $src (@$src) { push(@result,$XHTML ? qq(<link rel="$rel" type="$type" href="$src" $other/>) - : qq(<link rel="$rel" type="$type" href="$src"$other>)) if $src; + : qq(<link rel="$rel" type="$type" href="$src" $other>)) if $src; } } else { # Otherwise, push the single -src, if it exists. push(@result,$XHTML ? qq(<link rel="$rel" type="$type" href="$src" $other/>) - : qq(<link rel="$rel" type="$type" href="$src"$other>) + : qq(<link rel="$rel" type="$type" href="$src" $other>) ) if $src; } + push(@result,$self->_condStyle($cond, $type, $rel, $other, @other)) if $cond; if ($verbatim) { my @v = ref($verbatim) eq 'ARRAY' ? @$verbatim : $verbatim; push(@result, "<style type=\"text/css\">\n$_\n</style>") foreach @v; @@ -1748,13 +1748,140 @@ } else { my $src = $s; push(@result,$XHTML ? qq(<link rel="$rel" type="$type" href="$src" $other/>) - : qq(<link rel="$rel" type="$type" href="$src"$other>)); + : qq(<link rel="$rel" type="$type" href="$src" $other>)); } } @result; } END_OF_FUNC +### Method: _condStyle +# internal method for generating conditional sections containing CSS style elements +# Responsible for unpacking arrays containing mixed +#### +'_condStyle' => <<'END_OF_FUNC', +sub _condStyle { + my ($self,$cond, $type, $rel, $other, @other) = @_; + + if (ref($cond) eq 'ARRAY') { + my (@result, @sheets); + if ($cond->[0] =~ /^!?[glteq<>=]*\s*(?:ie\s*)?\d+(?:\.\d+)?$/i) { + push @sheets, shift @$cond; + } + foreach $c (@$cond) { + if (ref $c) { + if (ref $c eq 'HASH' && !defined($c->{'-expr'})) { + push(@sheets, $c); + } else { + push(@result,$self->_condSheets($c, $type, $rel, $other, @other)) if $c; + } + } else { + push(@sheets, $c); + } + } + # unshift to put the general IE style before the more specific style + unshift(@result,$self->_condSheets(\@sheets, $type, $rel, $other, @other)) if @sheets; + return @result; + } else { + return $self->_condSheets($cond, $type, $rel, $other, @other); + } +} +END_OF_FUNC + +### Method: _sheet +# internal method for generating an external or header style, based on content +# content can be: a scalar +### +'_sheet' => <<'END_OF_FUNC', +sub _sheet ($$;$$$) { + my ($self,$content, $type, $rel, $other) = @_; + if ($content =~ m%/\*.*\*/|{(?:\s*[-_a-z]*:.*;?)*\s*}%) { + # can't use CDATA + # $cdata_(start|end) are locally defined elsewhere + return style({'type'=>$type},"$cdata_start\n$content\n$cdata_end"); + } else { + return $XHTML ? qq(<link rel="$rel" type="$type" href="$content" $other/>) + : qq(<link rel="$rel" type="$type" href="$content" $other>) + } +} +END_OF_FUNC + +### Method: _condSheets +# internal method for generating a conditional section containing CSS style sections +# _condSheets($CGI, $cond, $type, $rel, $other, @other) +# If $cond is an array ref, all elements must be scalars or hashes with no +# '-expr' key; the 1st element may be an expression. if $cond is a hash, it is +# passed through _style; it should not contain any '-cond' sections +#### +'_condSheets' => <<'END_OF_FUNC', +sub _condSheets { + my ($self,$cond, $type, $rel, $other, @other) = @_; + my $expr = 'IE'; + + # can't use HTML comments within a conditional comment, so need different $cdata_.* + local $cdata_start = $XHTML ? "\n/* <![CDATA[ */" : ''; + local $cdata_end = $XHTML ? "\n/* ]]> */\n" : ''; + + if (ref $cond) { + if (ref $cond eq 'HASH' && $cond->{'-expr'}) { + $expr = delete $cond->{'-expr'}; + } elsif (ref $cond eq 'ARRAY' + && $cond->[0] =~ /^(?:[ l<g>n]?t?[>e=]?\s*(?:ie\s*)?(?:\d(?:\.\d+)?)|[ !()|&]+|false|true)+$/i) + { + # above RE is more permissive than the expression grammer, but still + # won't match URIs or style sheets (no false positives). Here's a more + # restrictive RE that has false negatives: + # /^(?:\s*!?\s*\(?[ glt<>=!]*[=e]?\s*(?:ie\s*)?(?:\d+(?:\.\d+)?|true|false|[&|])\)?\s*[&|]?)+$/i + # eg '(7|(9|5.5))'. Of course, the likelihood of these more complex + # expressions is low. + $expr = shift @$cond; + } + $expr =~ s/eq|==//gi; # strip 'eq' and '==' + $expr =~ s/(?:!=|ne|<>)\s*/!/gi; # replace '!=', 'ne' and '<>' with '!' + $expr =~ s/([gl])e/$1te/g; # replace 'le' and 'ge' with 'lte' and 'gte' + $expr =~ s/</lt/g; # + $expr =~ s/>/gt/g; # + $expr =~ s/=\s*/e /g; # + # insert "IE" where it's missing + $expr =~ s/(^|[gl!]t?e?)\s*(\d)|(!)\s*(?:\)|$)/$1$3 IE $2/gi; + $expr =~ s/^\s+|\s+$//g; + } + my $uplevel = '--'; + # if expression is '! IE', then the conditional comment is targeting non-IE + # browsers, so we want a "downlevel revealed" conditional comment (ie not a comment) + if ($expr =~ /^!\s*IE$/) { + $uplevel = ''; + } + + my @result = ("<!${uplevel}[if $expr]>"); + if (ref $cond eq 'ARRAY') { + foreach my $sheet (@$cond) { + if (ref($sheet) eq 'HASH') { + $sheet->{'-type'} ||= $type; + $sheet->{'-alternate'} = 1 if $rel ne 'stylesheet'; + my %other = @other; + @{$sheet}{keys %other} = values %other; + # sanity check for $sheet->{'-cond'}? + push @result, $self->_style($sheet, $cdata_start, $cdata_end); + } else { + push @result, $self->_sheet($sheet, $type, $rel, $other); + } + } + } elsif (ref $cond eq 'HASH') { + $cond->{'-type'} ||= $type; + $cond->{'-alternate'} = 1 if $rel ne 'stylesheet'; + my %other = @other; + @{$cond}{keys %other} = values %other; + push @result, $self->_style($cond, $cdata_start, $cdata_end); + } else { + push @result, $self->_sheet($cond, $type, $rel, $other); + } + push @result, "<![endif]$uplevel>"; + return @result; +} +END_OF_FUNC + + '_script' => <<'END_OF_FUNC', sub _script { my ($self,$script) = @_; @@ -7133,14 +7260,83 @@ start_html() method a B<-style> parameter. The value of this parameter may be a scalar, in which case it is treated as the source URL for the stylesheet, or it may be a hash reference. In the latter -case you should provide the hash with one or more of B<-src> or -B<-code>. B<-src> points to a URL where an externally-defined +case you should provide the hash with one or more of B<-src>, B<-code> +or B<-cond>. B<-src> points to a URL where an externally-defined stylesheet can be found. B<-code> points to a scalar value to be incorporated into a <style> section. Style definitions in B<-code> override similarly-named ones in B<-src>, hence the name "cascading." -You may also specify the type of the stylesheet by adding the optional -B<-type> parameter to the hash pointed to by B<-style>. If not +B<-cond> will wrap the style sheet(s) in a conditional comment. The +basic values of a B<-cond> can be a string, an array or a hash. +A string is a URI path or CSS rules (CGI.pm will automatically +pick which). A hash is similar to what you might pass to a B<-style> +argument, but it cannot contain any B<-cond> and can optionally contain +a B<-expr> argument, which becomes the conditional expression +("<!--[if $expr]>"). An array starts with an optional expression; if +absent, it defaults to the expression 'IE'. + +You can also pass B<-cond> an array of basic values; this array can also +start with an optional conditional expression, which becomes the expression +for all scalars and hashes missing and '-expr' key, but not for arrays (see +complex example below). + +Conditional expressions are slightly expanded from what IE accepts: you +can use symbolic comparisons (eg '<' for 'lt'; '<>7', '!=7' or 'ne 7' +for '! IE 7') and don't need to include the 'IE' feature (eg 'gte 6' for +'gte IE 6'). The expanded form will be translated to one IE handles. + +A B<-cond> can be as simple as: + -cond => '/style/site_IE.css' +which produces: + <!--[if IE]> + <link rel="stylesheet" type="text/css" href="/style/site_IE.css" /> + <![endif]--> +and can be as complex as: + -cond => [5.5, '.foo {color: navy;}', '/style/1_IE.css', + [5, '/style/IE5.css'], + {-src => ['/style/2_IE.css', '/style/3_IE.css'], + -code => '.bar {padding: 1em;}'}, + ['/style/4.css'], + ['!', '/style/notIE.css'], + {-expr => '((>=7)|(! 5)&IE', + -src => '/style/complex_IE.css', + -code => '.foo, .bar { border: #800;}'} + ] +which produces: + <!--[if IE 5.5]> + <style type="text/css"> + .foo {color: navy;} + </style> + <link rel="stylesheet" type="text/css" href="/style/1_IE.css" > + <link rel="stylesheet" type="text/css" href="/style/2_IE.css" > + <link rel="stylesheet" type="text/css" href="/style/3_IE.css" > + <style type="text/css"> + <!-- + .bar {padding: 1em;} + --> + </style> + <![endif]--> + <!--[if IE 5]> + <link rel="stylesheet" type="text/css" href="/style/IE5.css" > + <![endif]--> + <!--[if IE]> + <link rel="stylesheet" type="text/css" href="/style/4.css" > + <![endif]--> + <![if ! IE]> + <link rel="stylesheet" type="text/css" href="/style/notIE.css" > + <![endif]> + <!--[if ((gte IE 7)|(! IE 5)&IE]> + <link rel="stylesheet" type="text/css" href="/style/complex_IE.css" > + <style type="text/css"> + <!-- + .foo, .bar { border: #800;} + --> + </style> + <![endif]--> + + +You may also specify the type of the stylesheet(s) by adding the optional +B<-type> parameter to the hash pointed to by B<-style>. If not specified, the style defaults to 'text/css'. To refer to a style within the body of your document, add the @@ -7219,13 +7415,16 @@ incorporated into the <link> tag. For example: start_html(-style=>{-src=>['/styles/print.css','/styles/layout.css'], + -cond=>'/styles/IE.css', -media => 'all'}); This will give: <link rel="stylesheet" type="text/css" href="/styles/print.css" media="all"/> <link rel="stylesheet" type="text/css" href="/styles/layout.css" media="all"/> - + <!--[if IE]> + <link rel="stylesheet" type="text/css" href="/styles/IE.css" media="all"/> + <![endif]--> <p> To make more complicated <link> tags, use the Link() function
Lincoln, I'd like your opinion on adding new features and enhancements to the HTML generation code in CGI.pm. It's not an area of the code base I'm personally interested in expanding. Mark On Fri Aug 01 10:39:02 2008, outis wrote: Show quoted text
> Patch is to add preliminary support (& documentation) for conditional > comments around style sheets. With the patch, the -style option accepts > a -cond argument. For example, > print start_html(-style => { > -media => 'all', > -cond => ['.foo {color: navy;}', '/style/1_IE.css', > [5, '/style/IE5.css'], > {-src => ['/style/2_IE.css', '/style/3_IE.css'], > -code => '.bar {padding: 1em;}'}, > ['/style/4.css'], > ['!', '/style/notIE.css'], > {-expr => '((>=7)|(! 5)&IE', > -src => '/style/complex_IE.css', > -code => '.foo, .bar { border: #800;}'} > ] > } > ); > produces > <!--[if IE]> > <style type="text/css"> > .foo {color: navy;} > </style> > <link rel="stylesheet" type="text/css" href="/style/1_IE.css"
media="all"> Show quoted text
> <link rel="stylesheet" type="text/css" href="/style/2_IE.css"
media="all"> Show quoted text
> <link rel="stylesheet" type="text/css" href="/style/3_IE.css"
media="all"> Show quoted text
> <style type="text/css"> > <!-- > .bar {padding: 1em;} > --> > </style> > <![endif]--> > <!--[if IE 5]> > <link rel="stylesheet" type="text/css" href="/style/IE5.css"
media="all"> Show quoted text
> <![endif]--> > <!--[if IE]> > <link rel="stylesheet" type="text/css" href="/style/4.css" media="all"> > <![endif]--> > <![if ! IE]> > <link rel="stylesheet" type="text/css" href="/style/notIE.css" > > <![endif]> > <!--[if ((gte IE 7)|(! IE 5)&IE]> > <link rel="stylesheet" type="text/css" href="/style/complex_IE.css" > media="all"> > <style type="text/css"> > <!-- > .foo, .bar { border: #800;} > --> > </style> > <![endif]--> > > Coming eventually: conditional comments for scripts and arbitrary header > elements.
This issue has been copied to: https://github.com/leejo/CGI.pm/issues/56 please take all future correspondence there. This ticket will remain open but please do not reply here. This ticket will be closed when the github issue is dealt with.
Rejecting. I'm not accepting any feature requests, patches, or updates to the HTML generating functions of CGI.pm unless they are of a critical nature (i.e. fixing fundamentally broken HTML output).