File Coverage

blib/lib/Mojo/DOM/Role/Style.pm
Criterion Covered Total %
statement 59 69 85.5
branch 12 16 75.0
condition 21 26 80.7
subroutine 11 12 91.6
pod 1 1 100.0
total 104 124 83.8


line stmt bran cond sub pod time code
1 4     4   1650108 use strict;
  4         5  
  4         132  
2 4     4   13 use warnings;
  4         8  
  4         507  
3              
4             package Mojo::DOM::Role::Style::Value;
5              
6             # use Scalar::Util qw/blessed/;
7              
8             use overload
9 5     5   168 '""' => sub { ${$_[0]}[0] },
  5         33  
10 6     6   10 '%{}' => sub { ${$_[0]}[1] },
  6         32  
11 4     4   24 fallback => 1;
  4         25  
  4         38  
12              
13             sub new {
14 11     11   22 my ($class, $css, $hash) = @_;
15 11         80 return bless [$css, $hash], $class;
16             }
17              
18             package Mojo::DOM::Role::Style;
19              
20             # ABSTRACT: Adds a style method to Mojo::DOM
21              
22 4     4   509 use Mojo::Base -role;
  4         14  
  4         30  
23 4     4   2203 use List::Util qw/uniq/;
  4         6  
  4         4145  
24              
25             sub style {
26 16     16 1 4508 my $self = shift;
27              
28 16   100     60 my $css = $self->attr('style') // '';
29 16         235 my ($h, $k) = _from_css($css);
30              
31             # Getter - no args
32 16 100       47 return Mojo::DOM::Role::Style::Value->new($css, $h) unless @_;
33              
34             # Single string arg - get one property value
35 5 100 100     53 if (@_ == 1 && !ref $_[0] && defined $_[0] && $_[0] !~ /:/) {
      100        
      66        
36 1         10 return $h->{$_[0]};
37             }
38              
39 4 50 100     13 if (@_ == 1 && !ref $_[0] && defined $_[0] && $_[0] =~ /:/) {
      66        
      33        
40 0         0 $self->attr(style => $_[0]);
41 0         0 return $self
42             }
43              
44             # Undef - remove style attribute entirely
45 4 100 100     10 if (@_ == 1 && !defined $_[0]) {
46 1         3 $self->attr({style => undef});
47 1         15 return $self;
48             }
49              
50             # Hashref - merge into existing style
51 3 100 66     11 if (@_ == 1 && ref $_[0] eq 'HASH') {
52 2         3 $h->{$_} = $_[0]->{$_} for keys %{$_[0]};
  2         6  
53 2         5 my $new = _to_css($h, $k);
54 2         6 $self->attr(style => $new);
55             # return Mojo::DOM::Role::Style::Value->new($new, $h);
56 2         37 return $self
57             }
58              
59             # Flat list - replace entire style
60 1 50       4 if (@_ % 2 == 0) {
61 1         2 my @keys = do { my %seen; grep { !$seen{$_}++ } @_[ grep { !($_ % 2) } 0 .. $#_ ] };
  1         1  
  1         2  
  1         4  
  2         4  
62 1         3 my $new = _to_css({@_}, \@keys);
63 1         4 $self->attr(style => $new);
64             # return Mojo::DOM::Role::Style::Value->new($new, {@_});
65 1         17 return $self;
66             }
67             }
68              
69             sub _to_css {
70 3     3   3 my ($h, $k) = @_;
71 3         13 $k = [ uniq(@$k, keys %$h) ];
72 3         6 return join ';', map { $_ . ':' . $h->{$_} } @$k;
  7         19  
73             }
74              
75             sub __from_css {
76 0     0   0 my $css = shift;
77 0 0       0 return ({}, []) unless $css;
78              
79 0         0 my $k = [ map { /^([^:]+?)\s*:/; $1 } split /\s*;\s*/, $css ];
  0         0  
  0         0  
80 0         0 my $h = { map { split /\s*:\s*/, $_, 2 } split /\s*;\s*/, $css };
  0         0  
81              
82 0         0 return ($h, $k);
83             }
84              
85             sub _from_css {
86 16     16   23 my $css = shift;
87 16 100       31 return ({}, []) unless $css;
88              
89 15         126 my @declarations = split /\s*;\s*(?![^(]*\))/, $css;
90              
91 15         27 my $k = [ map { /^([^:]+?)\s*:/; $1 } @declarations ];
  41         131  
  41         89  
92 15         24 my $h = { map { split /\s*:\s*/, $_, 2 } @declarations };
  41         133  
93              
94 15         43 return ($h, $k);
95             }
96              
97             1;
98              
99             =encoding utf8
100              
101             =head1 NAME
102              
103             Mojo::DOM::Role::Style - Manage inline CSS styles on Mojo::DOM elements
104              
105             =head1 SYNOPSIS
106              
107             use Mojo::DOM;
108              
109             my $dom = Mojo::DOM->new('
hello
')
110             ->with_roles('+Style');
111              
112             # Get the full style - stringifies to the CSS string
113             my $style = $dom->at('div')->style;
114             say $style; # "color:red;font-size:12pt"
115             say $style->{'color'}; # "red"
116              
117             # Get a single property
118             my $color = $dom->at('div')->style('color');
119              
120             # Replace with a raw CSS string
121             $dom->at('div')->style('color:blue;font-size:14pt');
122              
123             # Replace with a flat list
124             $dom->at('div')->style(color => 'blue', 'font-size' => '14pt');
125              
126             # Merge into the existing style
127             $dom->at('div')->style({'font-weight' => 'bold'});
128              
129             # Remove the style attribute entirely
130             $dom->at('div')->style(undef);
131              
132             =head1 DESCRIPTION
133              
134             L is a role that adds a convenience method for
135             reading and manipulating the C