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

use 5.008;
use base qw'RDF::RDFa::Generator::HTML::Hidden';
use strict;
use constant XHTML_NS => 'http://www.w3.org/1999/xhtml';
use Encode qw'encode_utf8';
use Icon::FamFamFam::Silk;
use RDF::RDFa::Generator::HTML::Pretty::Note;
use XML::LibXML qw':all';

our $VERSION = '0.103';

sub create_document
{
	my ($proto, $model, %opts) = @_;
	my $self = (ref $proto) ? $proto : $proto->new;
	
	my $html = sprintf(<<HTML, ($self->{'version'}||'1.0'), ($self->{'title'} || 'RDFa Document'), ref $self);
<html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa %1\$s">
<head profile="http://www.w3.org/1999/xhtml/vocab">
<title>%2\$s</title>
<meta name="generator" value="%3\$s" />
</head>
<body>
<h1>%2\$s</h1>
<p><small>Generated by %3\$s.</small></p>
</body>
</html>
HTML

	return $proto->inject_document($html, $model, %opts);
}

sub nodes
{
	my ($proto, $model, %opts) = @_;
	my $self = (ref $proto) ? $proto : $proto->new;
	
	my $stream = $self->_get_stream($model);
	my @nodes;
	
	my $root_node = XML::LibXML::Element->new('div');
	$root_node->setNamespace(XHTML_NS, undef, 1);
	
	my $prefixes = RDF::Prefixes->new($self->{namespaces});
	my $subjects = {};
	while (my $st = $stream->next)
	{
		next if $st->subject->is_literal;  # ???
		my $s = $st->subject->is_resource ?
			$st->subject->uri :
			('_:'.$st->subject->blank_identifier);
		push @{ $subjects->{$s} }, $st;
	}
	
	foreach my $s (sort keys %$subjects)
	{
		my $subject_node = $root_node->addNewChild(XHTML_NS, 'div');
		
		my $id = _make_id($s, $opts{'id_prefix'});
		$subject_node->setAttribute('id', $id) if defined $id;
		
		$self->_process_subject($subjects->{$s}->[0], $subject_node, $prefixes);
		$self->_resource_heading($subjects->{$s}->[0]->subject, $subject_node, $subjects->{$s}, $prefixes);
		$self->_resource_classes($subjects->{$s}->[0]->subject, $subject_node, $subjects->{$s}, $prefixes);
		$self->_resource_statements($subjects->{$s}->[0]->subject, $subject_node, $subjects->{$s}, $prefixes, $opts{'interlink'}||0, $opts{'id_prefix'}, $model);
		$self->_resource_notes($subjects->{$s}->[0]->subject, $subject_node, $model, $opts{'notes_heading'}||'Notes', $opts{'notes'})
			if defined $opts{'notes'};
	}

	use Data::Dumper; Dumper($prefixes);
	
	if ($self->{'version'} == 1.1
	and $self->{'prefix_attr'})
	{
		$root_node->setAttribute('prefix', $prefixes->rdfa)
			if %$prefixes;
	}
	else
	{
		while (my ($p,$u) = each(%{$prefixes->to_hashref}))
		{
			$root_node->setNamespace($u, $p, 0);
		}
	}
	
	push @nodes, $root_node;
	return @nodes if wantarray;
	my $nodelist = XML::LibXML::NodeList->new;
	$nodelist->push(@nodes);
	return $nodelist;
}

sub _make_id
{
	my ($ident, $prefix) = @_;
	
	if ($prefix =~ /^[A-Za-z][A-Za-z0-9\_\:\.\-]*$/)
	{
		$ident =~ s/([^A-Za-z0-9\_\:\.])/sprintf('-%x-',ord($1))/ge;
		return $prefix . $ident;
	}
	
	return undef;
}

sub _resource_heading
{
	my ($self, $subject, $node, $statements, $prefixes) = @_;
	
	my $heading = $node->addNewChild(XHTML_NS, 'h3');
	$heading->appendTextNode( $subject->is_resource ? $subject->uri : ('_:'.$subject->blank_identifier) );
	$heading->setAttribute('class', $subject->is_resource ? 'resource' : 'blank' );
	
	return $self;
}

sub _resource_classes
{
	my ($self, $subject, $node, $statements, $prefixes) = @_;
	
	my @statements = sort {
		$a->predicate->uri cmp $b->predicate->uri
		or $a->object->uri cmp $b->object->uri
		}
		grep {
			$_->predicate->uri eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
			and $_->object->is_resource
		}
		@$statements;

	return unless @statements;

	my $SPAN = $node->addNewChild(XHTML_NS, 'span');
	$SPAN->setAttribute('class', 'rdf-type');
	$SPAN->setAttribute('rel', $self->_make_curie('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $prefixes));

	foreach my $st (@statements)
	{
		my $IMG = $SPAN->addNewChild(XHTML_NS, 'img');
		$IMG->setAttribute('about', $st->object->uri);
		$IMG->setAttribute('alt',   $st->object->uri);
		$IMG->setAttribute('src',   $self->_img($st->object->uri));
		$IMG->setAttribute('title', $st->object->uri);
	}

	return $self;
}


sub _resource_statements
{
	my ($self, $subject, $node, $statements, $prefixes, $interlink, $id_prefix, $model) = @_;
	
	my @statements = sort {
		$a->predicate->uri cmp $b->predicate->uri
		or $a->object->as_ntriples cmp $b->object->as_ntriples
		}
		grep {
			$_->predicate->uri ne 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
			or !$_->object->is_resource
		}
		@$statements;

	return unless @statements;
	
	my $DL = $node->addNewChild(XHTML_NS, 'dl');
	
	my $current_property = undef;
	foreach my $st (@statements)
	{
		unless ($st->predicate->uri eq $current_property)
		{
			my $DT = $DL->addNewChild(XHTML_NS, 'dt');
			$DT->setAttribute('title', $st->predicate->uri);
			$DT->appendTextNode($self->_make_curie($st->predicate->uri, $prefixes));
		}
		$current_property = $st->predicate->uri;
		
		my $DD = $DL->addNewChild(XHTML_NS, 'dd');
		
		if ($st->object->is_resource && $st->object->uri =~ /^javascript:/i)
		{
			$DD->setAttribute('class', 'resource');
			
			my $A = $DD->addNewChild(XHTML_NS, 'span');
			$A->setAttribute('rel',  $self->_make_curie($st->predicate->uri, $prefixes));
			$A->setAttribute('resource', $st->object->uri);
			$A->appendTextNode($st->object->uri);
		}
		elsif ($st->object->is_resource)
		{
			$DD->setAttribute('class', 'resource');
			
			my $A = $DD->addNewChild(XHTML_NS, 'a');
			$A->setAttribute('rel',  $self->_make_curie($st->predicate->uri, $prefixes));
			$A->setAttribute('href', $st->object->uri);
			$A->appendTextNode($st->object->uri);			
		}
		elsif ($st->object->is_blank)
		{
			$DD->setAttribute('class', 'blank');
			
			my $A = $DD->addNewChild(XHTML_NS, 'span');
			$A->setAttribute('rel',  $self->_make_curie($st->predicate->uri, $prefixes));
			$A->setAttribute('resource', '[_:'.$st->object->blank_identifier.']');
			$A->appendTextNode('_:'.$st->object->blank_identifier);
		}
		elsif ($st->object->is_literal
		&& !$st->object->has_datatype)
		{
			$DD->setAttribute('property',  $self->_make_curie($st->predicate->uri, $prefixes));
			$DD->setAttribute('class', 'plain-literal');
			$DD->setAttribute('xml:lang',  $st->object->literal_value_language);
			$DD->appendTextNode(encode_utf8($st->object->literal_value));
		}
		elsif ($self->{'safe_xml_literals'}
		&& $st->object->is_literal
		&& $st->object->has_datatype
		&& $st->object->literal_datatype eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral')
		{
			$DD->setAttribute('property',  $self->_make_curie($st->predicate->uri, $prefixes));
			$DD->setAttribute('class', 'typed-literal datatype-xmlliteral');
			$DD->setAttribute('datatype',  $self->_make_curie($st->object->literal_datatype, $prefixes));
			$DD->setAttribute('content', encode_utf8($st->object->literal_value));
			$DD->addNewChild(XHTML_NS, 'pre')->addNewChild(XHTML_NS, 'code')->appendTextNode(encode_utf8($st->object->literal_value));
		}
		elsif ($st->object->is_literal
		&& $st->object->has_datatype
		&& $st->object->literal_datatype eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral')
		{
			$DD->setAttribute('property',  $self->_make_curie($st->predicate->uri, $prefixes));
			$DD->setAttribute('class', 'typed-literal datatype-xmlliteral');
			$DD->setAttribute('datatype',  $self->_make_curie($st->object->literal_datatype, $prefixes));
			$DD->appendWellBalancedChunk(encode_utf8($st->object->literal_value));
		}
		elsif ($st->object->is_literal
		&& $st->object->has_datatype)
		{
			$DD->setAttribute('property',  $self->_make_curie($st->predicate->uri, $prefixes));
			$DD->setAttribute('class', 'typed-literal');
			$DD->setAttribute('datatype',  $self->_make_curie($st->object->literal_datatype, $prefixes));
			$DD->appendTextNode(encode_utf8($st->object->literal_value));
		}

		if ($interlink && !$st->object->is_literal)
		{
			if ($model->count_statements($st->object, undef, undef))
			{
				$DD->appendTextNode(' ');
				my $seealso = $DD->addNewChild(XHTML_NS, 'a');
				$seealso->setAttribute('about', $st->object->is_resource ? $st->object->uri : '[_:'.$st->object->blank_identifier.']');
				$seealso->setAttribute('rel', 'seeAlso');
				$seealso->setAttribute('href', '#'._make_id($st->object->is_resource ? $st->object->uri : '_:'.$st->object->blank_identifier, $id_prefix));
				$seealso->appendTextNode($interlink);
			}
		}
	}
	
	if ($interlink)
	{
		my $iter = $model->get_statements(undef, undef, $subject);
		if ($iter->peek)
		{
			my $seealsoDT = $DL->addNewChild(XHTML_NS, 'dt');
			$seealsoDT->setAttribute('class', 'seeAlso');
			$seealsoDT->appendTextNode($interlink);

			my $sadata = {};
			while (my $sast = $iter->next)
			{
				my $sas = $sast->subject->is_resource ? $sast->subject->uri : '_:'.$sast->subject->blank_identifier;
				my $p = $self->_make_curie($sast->predicate->uri, $prefixes);
				$sadata->{$sas}->{$p} = $sast->predicate->uri;
			}
			
			my $seealso = $DL->addNewChild(XHTML_NS, 'dd');
			$seealso->setAttribute('class', 'seeAlso');
			my @keys = sort keys %$sadata;
			foreach my $sas (@keys)
			{
				my $span = $seealso->addNewChild(XHTML_NS, 'span');
				$span->appendTextNode('is ');
				my @pkeys = sort keys %{$sadata->{$sas}};
				foreach my $curie (@pkeys)
				{
					my $i = $span->addNewChild(XHTML_NS, 'i');
					$i->appendTextNode($curie);
					$i->setAttribute(title => $sadata->{$sas}->{$curie});
					$span->appendTextNode( $curie eq $pkeys[-1] ? '' : ', ' );
				}
				$span->appendTextNode(' of ');
				my $a = $span->addNewChild(XHTML_NS, 'a');
				$a->setAttribute('about', $sas !~ /^_:/ ? $sas : '[_:'.$sas.']');
				$a->setAttribute('rel', 'seeAlso');
				$a->setAttribute('href', '#'._make_id($sas, $id_prefix));
				$a->appendTextNode($sas);
				$seealso->appendTextNode( $sas eq $keys[-1] ? '.' : '; ' );
			}
		}
	}
	
	return $self;
}

sub _resource_notes
{
	my ($self, $subject, $node, $model, $notes_heading, $notes) = @_;
	
	my @relevant;
	
	foreach my $note (@$notes)
	{
		push @relevant, $note
			if $note->is_relevant_to($subject);
	}
	
	if (@relevant)
	{
		my $heading = $node->addNewChild(XHTML_NS, 'h4');
		$heading->appendTextNode($notes_heading || 'Notes');

		my $list = $node->addNewChild(XHTML_NS, 'ul');

		foreach my $note (@relevant)
		{
			$list->appendChild( $note->node(XHTML_NS, 'li') );
		}
	}
	
	return $self;
}

sub _img
{
	my ($self, $type) = @_;
	
	if ($type eq 'urn:x-rdf-rdfa-linter:internals:OpenGraphProtocolNode')
	{
		return 'data:image/png;charset=binary;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oFEBYGBzcdoOEAAALhSURBVCjPJdLfb4trHADw7/d53nbr2661X9ZV29mE/VLMYplwchwnmCziR7iSEHbpQnAj7lxJJBIhJELcSBAh50iErAcJ2wnLRrJstQXdL7Sbtmpv173P3vd9nq8Ln7/hg3/t6BJCAEB9Q/2Na1d8Xt1YmsuaSVsKn7u6Sl/lZjoAIiIAEJG2aJpSyi2dnceOH87I4UfD12fyw7aJ0mIu3fHqZbHa7s4VxwLuECJDRA0Bund3vXz1cmWCjMyzz33uz31e4zsCANcg2Kimtz8Ya32xv+VC1N/BkPM//tzW03MEArN3b/1X6vJ8G+VKYmWUwjEyC8A0TPa7hVjMlsUbKjf7S2r4m//7YVl6sHB56q1uzGJkPYkC1HdQrEulxrAiAkgwMaC5y5ac6kRrdbfm83kfj9yYGtK4BvUdKhFnjENmEhbnubQwEcdwTK3rVh96S0KxkanIG61o56fyQzNDnrU7VfPf0jQwO4luDy1kcVmI5mehba8Mr6Ovw2wuoY83xrW8mHEslf+KP9P4/l+emUQ9QBURUJJsAVzDj694bpqQQW6GZcykJpUgQMeC+TQgQ2GAcjA1BqBAOuDYkE+hJcAyQdoglaWVuYPIHV8l1bVTzRo18pRbJoRalFVEZJCbxtadMhyj3kuat1L5Sqo1f0ltuR6qaTRSH7C2BQpZNGbBsTgiWSYW81j8gQs5MjK44YAT9W9iHF0bQwcbti6kEqzvJrcFlAagrl1tOSq95RQI0qd+9uKqFmpWVVG+KXyIAWBb8FBT0+rYHpGbZv7l5KugQgYKGSzmMdhITduULUDSUlfzmYf34khERJQujj4YPTk+mB154jHm0OUh7oKlAgKDtr2yPCpe34YTPacGBt4hEf1umBMTzycvJr68zk2UZpPMsdBbTsEWq2pF6a7m02ay7uy587Zto1Lqd10AcJSVLo6OZ3u/L35ylPC5qiL+9rbQvn8ePr9z9346lXKk/AUjmnS/afx+BwAAAABJRU5ErkJggg==';
	}
	
	my $icons = {
		'http://bblfish.net/work/atom-owl/2006-06-06/#Entry'   => 'page_white_link',
		'http://bblfish.net/work/atom-owl/2006-06-06/#Feed'    => 'feed',
		'http://commontag.org/ns#AuthorTag'                    => 'tag_green',
		'http://commontag.org/ns#AutoTag'                      => 'tag_red',
		'http://commontag.org/ns#ReaderTag'                    => 'tag_yellow',
		'http://commontag.org/ns#Tag'                          => 'tag_blue',
		'http://ontologi.es/doap-bugs#Bug'                     => 'bug',
		'http://purl.org/goodrelations/v1#PriceSpecification'  => 'money',
		'http://purl.org/NET/book/vocab#Book'                  => 'book',
		'http://purl.org/NET/c4dm/event.owl#Event'             => 'date',
		'http://purl.org/ontology/bibo/Book'                   => 'book',
		'http://purl.org/rss/1.0/channel'                      => 'feed',
		'http://purl.org/rss/1.0/item'                         => 'page_white_link' ,
		'http://purl.org/stuff/rev#Review'                     => 'award_star_gold_1',
		'http://rdf.data-vocabulary.org/#Organization'         => 'chart_organisation',
		'http://rdf.data-vocabulary.org/#Person'               => 'user',
		'http://rdf.data-vocabulary.org/#Review-aggregate'     => 'award_star_add',
		'http://rdf.data-vocabulary.org/#Review'               => 'award_star_gold_1',
		'http://schema.org/Person'                             => 'user_orange',
		'http://schema.org/Event'                              => 'date',
		'http://schema.org/FinancialService'                   => 'money',
		'http://schema.org/TennisComplex'                      => 'sport_tennis',
		'http://schema.org/Bakery'                             => 'cake',
		'http://schema.org/Map'                                => 'world',
		'http://schema.org/GolfClub'                           => 'sport_golf',
		'http://schema.org/CafeOrCoffeeShop'                   => 'cup',
		'http://schema.org/ProfilePage'                        => 'page_green',
		'http://usefulinc.com/ns/doap#Project'                 => 'application_double',
		'http://usefulinc.com/ns/doap#Version'                 => 'application_lightning',
		'http://www.holygoat.co.uk/owl/redwood/0.1/tags/Tagging' => 'tag_blue_add',
		'http://www.holygoat.co.uk/owl/redwood/0.1/tags/Tag'   => 'tag_blue',
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property'  => 'arrow_right',
		'http://www.w3.org/2000/01/rdf-schema#Class'           => 'cog',
		'http://www.w3.org/2002/12/cal/ical#Vcalendar'         => 'calendar',
		'http://www.w3.org/2002/12/cal/ical#Vevent'            => 'date',
		'http://www.w3.org/2002/07/owl#AnnotationProperty'     => 'arrow_right',
		'http://www.w3.org/2002/07/owl#AsymmetricProperty'     => 'arrow_right',
		'http://www.w3.org/2002/07/owl#Class'                  => 'cog',
		'http://www.w3.org/2002/07/owl#DatatypeProperty'       => 'arrow_right',
		'http://www.w3.org/2002/07/owl#DeprecatedProperty'     => 'arrow_right',
		'http://www.w3.org/2002/07/owl#FunctionalProperty'     => 'arrow_right',
		'http://www.w3.org/2002/07/owl#InverseFunctionalProperty' => 'arrow_right',
		'http://www.w3.org/2002/07/owl#IrreflexiveProperty'    => 'arrow_right',
		'http://www.w3.org/2002/07/owl#ObjectProperty'         => 'arrow_right',
		'http://www.w3.org/2002/07/owl#OntologyProperty'       => 'arrow_right',
		'http://www.w3.org/2002/07/owl#ReflexiveProperty'      => 'arrow_right',
		'http://www.w3.org/2002/07/owl#SymmetricProperty'      => 'arrow_right',
		'http://www.w3.org/2002/07/owl#TransitiveProperty'     => 'arrow_right',
		'http://www.w3.org/2003/01/geo/wgs84_pos#Point'        => 'world', 
		'http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing' => 'world',
		'http://www.w3.org/2004/02/skos/core#Concept'          => 'brick',
		'http://www.w3.org/2004/02/skos/core#ConceptScheme'    => 'bricks',
		'http://www.w3.org/2006/vcard/ns#Address'              => 'house',
		'http://www.w3.org/2006/vcard/ns#Location'             => 'world', 
		'http://www.w3.org/2006/vcard/ns#Vcard'                => 'vcard',
		'http://www.w3.org/ns/auth/rsa#RSAPublicKey'           => 'key',
		'http://xmlns.com/foaf/0.1/Agent'                      => 'user_gray',
		'http://xmlns.com/foaf/0.1/Document'                   => 'page_white_text',
		'http://xmlns.com/foaf/0.1/Group'                      => 'group',
		'http://xmlns.com/foaf/0.1/Image'                      => 'image',
		'http://xmlns.com/foaf/0.1/OnlineAccount'              => 'status_online',
		'http://xmlns.com/foaf/0.1/Organization'               => 'chart_organisation',
		'http://xmlns.com/foaf/0.1/Person'                     => 'user_green',
		'http://xmlns.com/foaf/0.1/PersonalProfileDocument'    => 'page_green',
	};
	
	return Icon::FamFamFam::Silk->new($icons->{$type}||'asterisk_yellow')->uri;
}

1;