The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package HTML::Native::Attributes;

# Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

=head1 NAME

HTML::Native::Attributes - HTML element attributes

=head1 SYNOPSIS

    use HTML::Native;

    my $elem = HTML::Native->new (
      a => { class => "active", href => "/home" },
      "Home"
    );
    my $attrs = \%$elem;
    print $attrs;
    # prints " class="active" href="/home""


    use HTML::Native::Attributes;

    my $attrs = HTML::Native::Attributes->new ( {
      class => "active",
      href => "/home",
    } );
    print $attrs;
    # prints " class="active" href="/home""

    $attrs->{class}->{nav} = 1;
    print $attrs;
    # prints " class="active nav" href="/home""

=head1 DESCRIPTION

An L<HTML::Native::Attributes> object represents a set of HTML element
attributes belonging to an L<HTML::Native> object.  It will be created
automatically by L<HTML::Native> as necessary; you probably do B<not>
ever need to manually create an L<HTML::Native::Attributes> object.

An L<HTML::Native::Attributes> object is a tied hash (see L<perltie>).
You can treat it as a normal Perl hash:

    my $attrs = HTML::Native::Attributes->new ( { href => "/home" } );
    print $attrs->{home};
    # prints "/home"

Any value stored in the hash will be automatically converted into a
new L<HTML::Native::Attribute> object, and can be transparently
accessed either as a scalar, or as a hash, or as an array.  For
example:

    my $attrs = HTML::Native::Attributes->new();
    $attrs->{class} = "error";
    $attrs->{class}->{fatal} = 1;
    push @{$attrs->{class}}, "internal";
    print $attrs->{class};
    # prints "error fatal internal";

See L<HTML::Native::Attribute> for more documentation and examples.

=cut

use HTML::Native::Attribute;
use Scalar::Util qw ( blessed );
use strict;
use warnings;

use overload
    '""' => sub { my $self = shift; return $self->attributes; },
    fallback => 1;

sub new {
  my $old = shift;
  $old = tied ( %$old ) // $old if ref $old;
  my $class = ref $old || $old;
  my $self = shift || {};

  my $hash;
  tie %$hash, $class, $self;
  bless $hash, $class;
  return $hash;
}

sub TIEHASH {
  my $old = shift;
  my $class = ref $old || $old;
  my $self = shift || {};

  bless $self, $class;

  # Convert unblessed values to HTML::Native::Attribute
  foreach my $value ( values %$self ) {
    $value = $self->new_attribute ( $value ) unless blessed $value;
  }

  return $self;
}

sub FETCH {
  my $self = shift;
  my $key = shift;

  return $self->{$key};
}

sub STORE {
  my $self = shift;
  my $key = shift;
  my $value = shift;

  # Convert unblessed values to HTML::Native::Attribute
  $value = $self->new_attribute ( $value ) unless blessed $value;

  $self->{$key} = $value;
}

sub DELETE {
  my $self = shift;
  my $key = shift;

  return delete $self->{$key};
}

sub CLEAR {
  my $self = shift;

  %$self = ();
}

sub EXISTS {
  my $self = shift;
  my $key = shift;

  return exists $self->{$key};
}

sub FIRSTKEY {
  my $self = shift;

  keys %$self;
  return each %$self;
}

sub NEXTKEY {
  my $self = shift;

  return each %$self;
}

sub SCALAR {
  my $self = shift;

  return scalar %$self;
}

sub attributes {
  my $self = shift;
  $self = tied ( %$self ) // $self if ref $self;

  return "" unless %$self;
  return " ".join ( " ", map { $_."=\"".$self->{$_}."\"" } sort keys %$self );
}

=head1 SUBCLASSING

When subclassing L<HTML::Native::Attributes>, you may wish to override
the class that is used by default to hold new attributes.  You can do
this by overriding the C<new_attribute()> method:

=head2 new_attribute()

    $attr = $self->new_attribute ( <value> )

The default implementation of this method simply calls
C<< HTML::Native::Attribute->new() >>:

    return HTML::Native::Attribute->new ( shift );

=cut

sub new_attribute {
  my $self = shift;
  $self = tied ( %$self ) // $self if ref $self;
  my $value = shift;

  return HTML::Native::Attribute->new ( $value );
}

1;