summaryrefslogtreecommitdiff
path: root/lib/gcstar/GCPlugins/GCboardgames
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gcstar/GCPlugins/GCboardgames')
-rw-r--r--lib/gcstar/GCPlugins/GCboardgames/GCReservoirJeux.pm418
-rw-r--r--lib/gcstar/GCPlugins/GCboardgames/GCboardgamegeek.pm278
-rw-r--r--lib/gcstar/GCPlugins/GCboardgames/GCboardgamesCommon.pm58
-rw-r--r--lib/gcstar/GCPlugins/GCboardgames/GCtrictrac.pm462
4 files changed, 1216 insertions, 0 deletions
diff --git a/lib/gcstar/GCPlugins/GCboardgames/GCReservoirJeux.pm b/lib/gcstar/GCPlugins/GCboardgames/GCReservoirJeux.pm
new file mode 100644
index 0000000..a1a4500
--- /dev/null
+++ b/lib/gcstar/GCPlugins/GCboardgames/GCReservoirJeux.pm
@@ -0,0 +1,418 @@
+package GCPlugins::GCboardgames::GCReservoirJeux;
+
+###################################################
+#
+# Copyright 2005-2010 Christian Jodar
+#
+# This file is part of GCstar.
+#
+# GCstar 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
+# (at your option) any later version.
+#
+# GCstar 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 GCstar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+#
+###################################################
+
+use strict;
+
+use GCPlugins::GCboardgames::GCboardgamesCommon;
+
+{
+ package GCPlugins::GCboardgames::GCPluginReservoirJeux;
+
+ use base qw(GCPlugins::GCboardgames::GCboardgamesPluginsBase);
+
+ sub start
+ {
+ my ($self, $tagname, $attr, $attrseq, $origtext) = @_;
+
+ $self->{inside}->{$tagname}++;
+
+ if ($self->{parsingEnded})
+ {
+ return;
+ }
+
+
+ if ($self->{parsingList})
+ {
+ # Parse the search results here
+ if (($tagname eq "h3") && ($attr->{class} =~ /^rusearch_result/))
+ {
+ $self->{itemIdx}++;
+ $self->{isBoardgame} = 1;
+ $self->{insideName} = 1;
+ }
+ if ($self->{isBoardgame})
+ {
+ if (($tagname eq "a") && ($attr->{href} ne "#") && ($attr->{class} =~ /^lien_item/))
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{url} = $attr->{href};
+ $self->{isBoardgame} = 0;
+ }
+ }
+ }
+ else
+ {
+ # Parse the items page here. Basically we do this by seaching for tags which match certain criteria, then preparing to grab
+ # the text inside these tags
+
+ if (($tagname eq "h1"))
+ {
+ $self->{insideName} = 1;
+ }
+ elsif (($tagname eq "div"))
+ {
+ if ($attr->{id} eq "fiche_technique_image")
+ {
+ $self->{insideImage} = 1;
+ }
+ elsif ($attr->{id} eq "bloc_centre_extensions")
+ {
+ $self->{insideExpansionList} = 1;
+ }
+ elsif ($attr->{id} eq "bloc_centre_extensions_bottom")
+ {
+ $self->{insideExpansionList} = 0;
+ }
+ elsif ($attr->{class} eq "fiche_technique_sep")
+ {
+ $self->{insideCategoryRow} = 0;
+ $self->{insideMechanicRow} = 0;
+ }
+
+ }
+ elsif ($tagname eq "img")
+ {
+ if ($self->{insideImage})
+ {
+ $self->{curInfo}->{boxpic} = "http://www.reservoir-jeux.com".$attr->{src} if ! $self->{curInfo}->{boxpic};
+ $self->{insideImage} = 0;
+ }
+ if ($self->{insideExpansionList})
+ {
+ $self->{curInfo}->{expandedby} .= $attr->{alt}.','
+ }
+ }
+ elsif ($tagname eq "a")
+ {
+ if ($attr->{class} eq "lien_item")
+ {
+ if ($self->{nextIsExpands})
+ {
+ $self->{insideExpands} = 1;
+ $self->{nextIsExpands} = 0;
+ }
+
+ if ($attr->{href} =~ /type=editeur/)
+ {
+ $self->{insidePublisher} = 1;
+ }
+ elsif ($attr->{href} =~ /type=auteur/)
+ {
+ $self->{insideDesigner} = 1;
+ }
+ elsif ($attr->{href} =~ /type=illustrateur/)
+ {
+ $self->{insideIllustrator} = 1;
+ }
+ elsif ($attr->{href} =~ /tag_id=/)
+ {
+ if ($self->{insideMechanicRow})
+ {
+ $self->{insideMechanic} = 1;
+ }
+ elsif ($self->{insideCategoryRow})
+ {
+ $self->{insideCategory} = 1;
+ }
+ }
+ elsif ($attr->{href} =~ /type=illustrateur/)
+ {
+ $self->{insideIllustrator} = 1;
+ }
+
+
+ }
+ }
+ elsif (($tagname eq "span") && ($attr->{class} eq "prod_description"))
+ {
+ $self->{insideDescription} = 1;
+ }
+
+ if ($tagname eq "br")
+ {
+ if($self->{insideDesignerRow})
+ {
+ $self->{curInfo}->{designedby} =~ s/\s\x2d\s$//g;
+ $self->{insideDesignerRow} = 0;
+ }
+ if($self->{insideIllustratorRow})
+ {
+ $self->{curInfo}->{illustratedby} =~ s/\s\x2d\s$//g;
+ $self->{insideIllustratorRow} = 0;
+ }
+ }
+
+ if ($self->{insideDescription})
+ {
+ if (($tagname eq "br") || ($tagname eq "p"))
+ {
+ # neatens up the description a little by starting new line on br tags
+ $self->{curInfo}->{description} .= "\n";
+ }
+ elsif ($tagname eq "li")
+ {
+ # basic formatting of lists
+ $self->{curInfo}->{description} .= " - ";
+ }
+ }
+ }
+ }
+
+ sub end
+ {
+ my ($self, $tagname) = @_;
+ $self->{inside}->{$tagname}--;
+
+ if ($self->{insideTechnicalDetails} && $tagname eq "div")
+ {
+ $self->{insideTechnicalDetails} = 0;
+ }
+ }
+
+ sub text
+ {
+ my ($self, $origtext) = @_;
+
+ return if (length($origtext) < 2);
+
+ $origtext =~ s/&#34;/"/g;
+ $origtext =~ s/&#179;/3/g;
+ $origtext =~ s/\n//g;
+ $origtext =~ s/^\s{2,//;
+ #French accents substitution
+ $origtext =~ s/&agrave;/à/;
+ $origtext =~ s/&Agrave;/À/;
+ $origtext =~ s/&eacute;/é/;
+
+ return if ($self->{parsingEnded});
+
+ if ($self->{parsingList})
+ {
+ if ($self->{isBoardgame} && $self->{insideName})
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{name} = $origtext;
+ $self->{insideName} = 0;
+ }
+
+ }
+ else
+ {
+ # Parse the text items page here.
+
+ if ($self->{insideName})
+ {
+ $self->{curInfo}->{name} = $origtext;
+ $self->{curInfo}->{name} =~ s/^\s+//;
+ $self->{curInfo}->{name} =~ s/\s+\Z//;
+ $self->{insideName} = 0;
+ }
+ if ($self->{inside}->{h2})
+ {
+ if ($origtext =~ /^Fiche technique/)
+ {
+ $self->{insideTechnicalDetails} = 1;
+ }
+ elsif ($origtext =~ /^M\xe9canismes/)
+ {
+ $self->{insideMechanicRow} = 1;
+ }
+ elsif ($origtext =~/^Th\xe8mes/)
+ {
+ $self->{insideCategoryRow} = 1;
+
+ }
+ }
+ if ($self->{insideTechnicalDetails})
+ {
+ if ($origtext =~ /^Date de sortie/)
+ {
+ $self->{curInfo}->{released} = $origtext;
+ $self->{curInfo}->{released} =~ s/Date de sortie : //g
+ }
+ elsif( $origtext =~ /Dur\xe9e : /)
+ {
+ $self->{curInfo}->{playingtime} = $origtext;
+ $self->{curInfo}->{playingtime} =~ s/\s*Dur\xe9e : //g;
+ }
+ elsif($origtext =~ /\xc0 partir de\s[0-9]*\sans/)
+ {
+ $self->{curInfo}->{suggestedage} = $origtext;
+ $self->{curInfo}->{suggestedage} =~ s/^\s*//g;
+ }
+ elsif ($origtext =~ /De [0-9]* \xe0 [0-9]* joueurs/)
+ {
+ $self->{curInfo}->{players} = $origtext;
+ $self->{curInfo}->{players} =~ s/^\s*De //g;
+ $self->{curInfo}->{players} =~ s/ joueurs//g;
+ }
+ }
+ if ($self->{insideDesigner})
+ {
+ # Append text (and trailing ,) to existing designer field
+ $self->{curInfo}->{designedby} .= $origtext." - ";
+ $self->{insideDesigner} = 0;
+ }
+ if ($self->{insideIllustrator})
+ {
+ # Append text (and trailing ,) to existing illustrator field
+ $self->{curInfo}->{illustratedby} .= $origtext." - ";
+ $self->{insideIllustrator} = 0;
+ }
+ if ($self->{insidePublisher})
+ {
+ $self->{curInfo}->{publishedby} = $origtext;
+ $self->{insidePublisher} = 0;
+ }
+ if ($self->{insideExpands})
+ {
+ $self->{curInfo}->{expansionfor} = $origtext;
+ $self->{insideExpands} = 0;
+ }
+ if ($self->{insideMechanic})
+ {
+ $self->{curInfo}->{mechanics} .= $self->capWord($origtext).',';
+ $self->{insideMechanic} = 0;
+ }
+ if ($self->{insideCategory})
+ {
+ $self->{curInfo}->{category} .= $self->capWord($origtext).',';
+ $self->{insideCategory} = 0;
+ }
+
+
+ if ($origtext =~ /^\s*Auteur(s)? : /)
+ {
+ $self->{insideDesignerRow} = 1;
+ }
+ if ($origtext =~ /^\s*Illustrateur(s)? : /)
+ {
+ $self->{insideIllustratorRow} = 1;
+ }
+ if ($origtext =~ /^Ce produit est une extension de :/)
+ {
+ $self->{nextIsExpands} = 1;
+ }
+ if ($self->{insideDescription})
+ {
+ $self->{curInfo}->{description} .= $origtext;
+ }
+ }
+ }
+
+ sub comment
+ {
+ my ($self, $comment) = @_;
+
+ if ($self->{parsingList})
+ {
+
+ }
+ else
+ {
+ if ($comment =~ /\/div/)
+ {
+ if($self->{insideDescription})
+ {
+ $self->{insideDescription} = 0;
+ # remove spaces from start and end of description
+ $self->{curInfo}->{description} =~ s/^\s+//;
+ $self->{curInfo}->{description} =~ s/\s+$//;
+ }
+ }
+ }
+ }
+
+ sub new
+ {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new();
+ bless ($self, $class);
+
+ $self->{hasField} = {
+ name => 1,
+ };
+
+ $self->{isBoardgame} = 0;
+ $self->{curName} = undef;
+ $self->{curUrl} = undef;
+
+ return $self;
+ }
+
+ sub preProcess
+ {
+ my ($self, $html) = @_;
+
+ $self->{parsingEnded} = 0;
+
+ $html =~ s/"&#34;/'"/g;
+ $html =~ s/&#34;"/"'/g;
+ $html =~ s|</a></b><br>|</a><br>|;
+
+ return $html;
+ }
+
+ sub getSearchUrl
+ {
+ my ($self, $word) = @_;
+
+ # Url returned below is the for the search page, where $word is replaced by the search
+ return ('http://www.reservoir-jeux.com/recherche.php', ['search' => $word, 'secteurid' => '-1', 'dv' => '30']);
+ }
+
+ sub getItemUrl
+ {
+ my ($self, $url) = @_;
+
+ return $url if $url =~ /^http:/;
+ if ($url =~ /^\//)
+ {
+ return "http://www.reservoir-jeux.com".$url;
+ }
+ else
+ {
+ return "http://www.reservoir-jeux.com/".$url;
+ }
+ }
+
+ sub getName
+ {
+ return "Reservoir Jeux";
+ }
+
+ sub getAuthor
+ {
+ return 'Florent';
+ }
+
+ sub getLang
+ {
+ return 'FR';
+ }
+
+
+}
+
+1;
diff --git a/lib/gcstar/GCPlugins/GCboardgames/GCboardgamegeek.pm b/lib/gcstar/GCPlugins/GCboardgames/GCboardgamegeek.pm
new file mode 100644
index 0000000..038198f
--- /dev/null
+++ b/lib/gcstar/GCPlugins/GCboardgames/GCboardgamegeek.pm
@@ -0,0 +1,278 @@
+package GCPlugins::GCboardgames::GCboardgamegeek;
+
+###################################################
+#
+# Copyright 2005-2010 Christian Jodar
+#
+# This file is part of GCstar.
+#
+# GCstar 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
+# (at your option) any later version.
+#
+# GCstar 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 GCstar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+#
+###################################################
+
+use strict;
+use utf8;
+
+use GCPlugins::GCboardgames::GCboardgamesCommon;
+
+{
+ package GCPlugins::GCboardgames::GCPluginboardgamegeek;
+
+ use base qw(GCPlugins::GCboardgames::GCboardgamesPluginsBase);
+ use XML::Simple;
+ use HTML::Entities;
+ use Encode;
+
+ sub parse
+ {
+ my ($self, $page) = @_;
+ return if $page =~ /^<!DOCTYPE html/;
+ my $xml;
+ my $xs = XML::Simple->new;
+
+ if ($self->{parsingList})
+ {
+ $xml = $xs->XMLin($page, ForceArray => ['boardgame'], KeyAttr => ['objectid']);
+ my $game;
+ foreach $game ( keys( %{ $xml -> {'boardgame'}} ) )
+ {
+ $self->{itemIdx}++;
+ $self->{itemsList}[$self->{itemIdx}]->{url} = "http://www.boardgamegeek.com/xmlapi/boardgame/".$game;
+ # Better check how the name is returned, the bgg api can be a little funny here
+ if (ref($xml->{'boardgame'}->{$game}->{'name'}))
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{name} = $xml->{'boardgame'}->{$game}->{'name'}->{'content'};
+ }
+ else
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{name} = $xml->{'boardgame'}->{$game}->{'name'};
+ }
+
+ if (!ref($xml->{'boardgame'}->{$game}->{'yearpublished'}))
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{released} = $xml->{'boardgame'}->{$game}->{'yearpublished'};
+ }
+ }
+ }
+ else
+ {
+ $xml = $xs->XMLin($page, ForceArray => ['name','boardgamedesigner','boardgameartist','boardgamepublisher',
+ 'boardgamecategory','boardgamemechanic','boardgameexpansion'],
+ KeyAttr => []);
+
+ $self->{curInfo}->{released} = $xml->{boardgame}->{yearpublished};
+ $self->{curInfo}->{released} =~ s/([^0-9])//g;
+ $self->{curInfo}->{players} = $xml->{boardgame}->{minplayers}."-".$xml->{boardgame}->{maxplayers};
+ $self->{curInfo}->{playingtime} = $xml->{boardgame}->{playingtime}." mins";
+ $self->{curInfo}->{suggestedage} = $xml->{boardgame}->{age};
+
+ my $primaryName = "";
+ for my $name (@{$xml->{boardgame}->{name}})
+ {
+ $primaryName = $name->{content}
+ if $name->{primary} eq "true";
+ }
+
+ if (($primaryName ne $self->{itemsList}[$self->{wantedIdx}]->{name})
+ && ($self->{itemsList}[$self->{wantedIdx}]->{name}))
+ {
+ # Name returned by boardgamegeek is different to the one the user selected
+ # this means they choose an translated name, so use the name they choose
+ # as the default, and put boardgamegeek's name in as the original (untranslated) name of the game
+ $self->{curInfo}->{name} = $self->{itemsList}[$self->{wantedIdx}]->{name};
+ $self->{curInfo}->{original} = $primaryName;
+ }
+ else
+ {
+ $self->{curInfo}->{name} = $primaryName;
+ }
+
+ # Have to decode the html type characters here
+ $self->{curInfo}->{description} = decode_entities($xml->{boardgame}->{description});
+ $self->{curInfo}->{description} =~ s/\<br\/>/\n/g;
+ $self->{curInfo}->{description} =~ s/<.*?>//g;
+
+ if ($self->{bigPics})
+ {
+ $self->{curInfo}->{boxpic} = $xml->{boardgame}->{image};
+ }
+ else
+ {
+ $self->{curInfo}->{boxpic} = $xml->{boardgame}->{thumbnail};
+ }
+
+ for my $designer (@{$xml->{boardgame}->{boardgamedesigner}})
+ {
+ $self->{curInfo}->{designedby} .= $designer->{content}.', ';
+ }
+ $self->{curInfo}->{designedby} =~ s/, $//;
+
+ for my $artist (@{$xml->{boardgame}->{boardgameartist}})
+ {
+ $self->{curInfo}->{illustratedby} .= $artist->{content}.', ';
+ }
+ $self->{curInfo}->{illustratedby} =~ s/, $//;
+
+ for my $publisher (@{$xml->{boardgame}->{boardgamepublisher}})
+ {
+ $self->{curInfo}->{publishedby} .= $publisher->{content}.', ';
+ }
+ $self->{curInfo}->{publishedby} =~ s/, $//;
+
+ for my $category (@{$xml->{boardgame}->{boardgamecategory}})
+ {
+ push @{$self->{curInfo}->{category}}, [$category->{content}];
+ }
+
+ for my $mechanic (@{$xml->{boardgame}->{boardgamemechanic}})
+ {
+ push @{$self->{curInfo}->{mechanics}}, [$mechanic->{content}];
+ }
+
+ for my $expansion (@{$xml->{boardgame}->{boardgameexpansion}})
+ {
+ if ($expansion->{inbound})
+ {
+ if ($self->{curInfo}->{expansionfor})
+ {
+ $self->{curInfo}->{expansionfor} .= ", ";
+ }
+ $self->{curInfo}->{expansionfor} .= $expansion->{content};
+ }
+ else
+ {
+ push @{$self->{curInfo}->{expandedby}}, [$expansion->{content}];
+ }
+ }
+ $self->{curInfo}->{web} = "http://boardgamegeek.com/boardgame/".$xml->{boardgame}->{objectid};
+ }
+ }
+
+ sub new
+ {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new();
+ bless ($self, $class);
+
+ $self->{hasField} = {
+ name => 1,
+ released => 1,
+ };
+
+ return $self;
+ }
+
+ sub getItemUrl
+ {
+ my ($self, $url) = @_;
+
+ if (!$url)
+ {
+ # If we're not passed a url, return a hint so that gcstar knows what type
+ # of addresses this plugin handles
+ $url = "http://www.boardgamegeek.com";
+ }
+ elsif (index($url,"xmlapi") < 0)
+ {
+ # Url isn't for the bgg api, so we need to find the game id
+ # and return a url corresponding to the api page for this game
+ $url =~ /\/([0-9]+)[\/]*/;
+ my $id = $1;
+ $url = "http://www.boardgamegeek.com/xmlapi/boardgame/".$id;
+ }
+ return $url;
+ }
+
+ sub preProcess
+ {
+ my ($self, $html) = @_;
+
+ return $html;
+ }
+
+ sub decodeEntitiesWanted
+ {
+ return 0;
+ }
+
+ sub getSearchUrl
+ {
+ my ($self, $word) = @_;
+ # Quick and dirty fixes because the bgg api struggles with some words. Should not be required anymore (7/6/2010)
+ # $word =~ s/the\+/\+/ig;
+ # $word =~ s/\+and+/\+/g;
+ # $word =~ s/\+of\+/\+/g;
+
+ return "http://www.boardgamegeek.com/xmlapi/search?search=$word";
+ }
+
+ sub changeUrl
+ {
+ my ($self, $url) = @_;
+ # Make sure the url is for the api, not the main movie page
+ return $self->getItemUrl($url);
+ }
+
+ sub getName
+ {
+ return "Board Game Geek";
+ }
+
+ sub getAuthor
+ {
+ return 'Zombiepig';
+ }
+
+ sub isPreferred
+ {
+ return 1;
+ }
+
+ sub getLang
+ {
+ return 'EN';
+ }
+
+ sub getCharset
+ {
+ my $self = shift;
+
+ return "UTF-8";
+ }
+
+ sub getSearchCharset
+ {
+ my $self = shift;
+
+ # Need urls to be double character encoded
+ return "utf8";
+ }
+
+ sub convertCharset
+ {
+ my ($self, $value) = @_;
+ return $value;
+ }
+
+ sub getNotConverted
+ {
+ my $self = shift;
+ return [];
+ }
+
+}
+
+1;
diff --git a/lib/gcstar/GCPlugins/GCboardgames/GCboardgamesCommon.pm b/lib/gcstar/GCPlugins/GCboardgames/GCboardgamesCommon.pm
new file mode 100644
index 0000000..088f077
--- /dev/null
+++ b/lib/gcstar/GCPlugins/GCboardgames/GCboardgamesCommon.pm
@@ -0,0 +1,58 @@
+package GCPlugins::GCboardgames::GCboardgamesCommon;
+
+###################################################
+#
+# Copyright 2005-2010 Christian Jodar
+#
+# This file is part of GCstar.
+#
+# GCstar 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
+# (at your option) any later version.
+#
+# GCstar 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 GCstar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+#
+###################################################
+
+use strict;
+
+use GCPlugins::GCPluginsBase;
+
+{
+ package GCPlugins::GCboardgames::GCboardgamesPluginsBase;
+
+ use base qw(GCPluginParser);
+
+ sub new
+ {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new();
+ bless ($self, $class);
+ return $self;
+ }
+
+ sub getSearchFieldsArray
+ {
+ return ['name'];
+ }
+
+ sub loadUrl
+ {
+ my ($self, $url) = @_;
+
+ $self->SUPER::loadUrl($url);
+
+ return $self->{curInfo};
+ }
+}
+
+1;
diff --git a/lib/gcstar/GCPlugins/GCboardgames/GCtrictrac.pm b/lib/gcstar/GCPlugins/GCboardgames/GCtrictrac.pm
new file mode 100644
index 0000000..e30e189
--- /dev/null
+++ b/lib/gcstar/GCPlugins/GCboardgames/GCtrictrac.pm
@@ -0,0 +1,462 @@
+package GCPlugins::GCboardgames::GCtrictrac;
+
+###################################################
+#
+# Copyright 2005-2010 Christian Jodar
+#
+# This file is part of GCstar.
+#
+# GCstar 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
+# (at your option) any later version.
+#
+# GCstar 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 GCstar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+#
+###################################################
+
+use strict;
+
+use GCPlugins::GCboardgames::GCboardgamesCommon;
+
+{
+ package GCPlugins::GCboardgames::GCPlugintrictrac;
+
+ use base qw(GCPlugins::GCboardgames::GCboardgamesPluginsBase);
+
+ sub start
+ {
+ my ($self, $tagname, $attr, $attrseq, $origtext) = @_;
+
+ $self->{inside}->{$tagname}++;
+
+ if ($self->{parsingEnded})
+ {
+ return;
+ }
+
+
+ if ($self->{parsingList})
+ {
+ # Parse the search results here
+
+ # Check if we are currently parsing an item page, not a search results page (ie - exact match has taken us straight to the page)
+ # Do this by checking if there is a heading on the page
+ if (($tagname eq "font") && ($attr->{style} =~ /FONT-SIZE: 20px/))
+ {
+ # Stop parsing results, switch to item parsing
+ $self->{parsingEnded} = 1;
+ $self->{itemIdx} = 0;
+ $self->{itemsList}[0]->{url} = $self->{loadedUrl};
+ }
+
+ # Quite easy to parse the search results page since all the information we need (url, title, year) is contained within the <a>
+ # tag for the image of each search result
+
+ # TODO - check how search results look when they do not have an image??
+
+ # Check if tag is an <a>, the url referenced is valid (not "#"), and the onmouseover text looks right
+ if (($tagname eq "a") && ($attr->{href} ne "#") && ($attr->{onmouseover} =~ /^(return overlib)/))
+ {
+
+ # Add to search results
+ $self->{itemIdx}++;
+ $self->{itemsList}[$self->{itemIdx}]->{url} = $attr->{href};
+
+ my $mouseoverText = $attr->{onmouseover};
+
+ # Parse some regular expressions to find the name and release date
+ if ($mouseoverText =~ /<b>(.+)<\/b>/)
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{name} = $1;
+ }
+ if ($mouseoverText =~ /<\/b> \((\d+)\)/)
+ {
+ $self->{itemsList}[$self->{itemIdx}]->{released} = $1;
+ }
+ }
+ }
+ else
+ {
+ # Parse the items page here. Basically we do this by seaching for tags which match certain criteria, then preparing to grab
+ # the text inside these tags
+
+ if (($tagname eq "font") && ($attr->{style} =~ /FONT-SIZE: 20px/))
+ {
+ $self->{insideName} = 1;
+ }
+ elsif (($tagname eq "font") && ($attr->{style} =~ /FONT-SIZE: 12px/))
+ {
+ if ($self->{nextIsPlayers})
+ {
+ $self->{insidePlayers} = 1;
+ $self->{nextIsPlayers} = 0;
+ }
+ if ($self->{nextIsAges})
+ {
+ $self->{insideAges} = 1;
+ $self->{nextIsAges} = 0;
+ }
+ if ($self->{nextIsPlayingTime})
+ {
+ $self->{insidePlayingTime} = 1;
+ $self->{nextIsPlayingTime} = 0;
+ }
+
+ }
+ elsif (($tagname eq "td") && ($attr->{height} eq "250") && ($attr->{width} eq "250"))
+ {
+ $self->{insideImage} = 1;
+ }
+ elsif ($tagname eq "img")
+ {
+ if ($self->{insideImage})
+ {
+ $self->{curInfo}->{boxpic} = "http://trictrac.net".$attr->{src} if ! $self->{curInfo}->{boxpic};
+ $self->{insideImage} = 0;
+ }
+ }
+ elsif ($tagname eq "a")
+ {
+ if ($self->{nextIsYear})
+ {
+ $self->{insideYear} = 1;
+ $self->{nextIsYear} = 0;
+ }
+ if ($self->{insideDesignerRow})
+ {
+ $self->{insideDesigner} = 1;
+ }
+ if ($self->{insideIllustratorRow})
+ {
+ $self->{insideIllustrator} = 1;
+ }
+ if ($self->{nextIsPublishers})
+ {
+ $self->{insidePublishers} = 1;
+ $self->{nextIsPublishers} = 0;
+ }
+ if ($self->{insideMechanicRow})
+ {
+ $self->{insideMechanic} = 1;
+ }
+ if ($self->{insideCategoryRow})
+ {
+ $self->{insideCategory} = 1;
+ }
+
+ }
+ elsif ($tagname eq "b")
+ {
+ if ($self->{insideExpansionList})
+ {
+ $self->{insideExpansion} = 1;
+ }
+ }
+ elsif (($tagname eq "p") && ( $attr->{style} =~ /TEXT-ALIGN: justify/))
+ {
+ $self->{insideDescription} = 1;
+ }
+ if ($self->{insideDescription})
+ {
+ if ($tagname eq "br")
+ {
+ # neatens up the description a little by starting new line on br tags
+ $self->{curInfo}->{description} .= "\n";
+ }
+ elsif ($tagname eq "li")
+ {
+ # basic formatting of lists
+ $self->{curInfo}->{description} .= " - ";
+ }
+ }
+ }
+ }
+
+ sub end
+ {
+ my ($self, $tagname) = @_;
+ $self->{inside}->{$tagname}--;
+ if ($tagname eq "tr")
+ {
+ if ($self->{insideDesignerRow})
+ {
+ # Use regex to strip final , off end of line
+ $self->{curInfo}->{designedby} =~ s/(, )$//;
+ $self->{insideDesignerRow} = 0;
+ }
+ if ($self->{insideIllustratorRow})
+ {
+ # Use regex to strip final , off end of line
+ $self->{curInfo}->{illustratedby} =~ s/(, )$//;
+ $self->{insideIllustratorRow} = 0;
+ }
+ if ($self->{insideMechanicRow})
+ {
+ $self->{insideMechanicRow} = 0;
+ }
+ if ($self->{insideCategoryRow})
+ {
+ $self->{insideCategoryRow} = 0;
+ }
+ }
+ elsif ($tagname eq "table")
+ {
+ if ($self->{insideExpansionList})
+ {
+ $self->{insideExpansionList} = 0;
+ }
+ }
+ elsif ($tagname eq "b")
+ {
+ if ($self->{insideExpands})
+ {
+ $self->{curInfo}->{expansionfor} =~ s/"//g;
+ $self->{insideExpands} = 0;
+ }
+ }
+ elsif (($tagname eq "td") && ($self->{insideDescription}))
+ {
+ $self->{insideDescription} = 0;
+ # remove spaces from start and end of description
+ $self->{curInfo}->{description} =~ s/^\s+//;
+ $self->{curInfo}->{description} =~ s/\s+$//;
+ }
+ }
+
+ sub text
+ {
+ my ($self, $origtext) = @_;
+
+ return if (length($origtext) < 2);
+
+ $origtext =~ s/&#34;/"/g;
+ $origtext =~ s/&#179;/3/g;
+ $origtext =~ s/\n//g;
+ $origtext =~ s/^\s{2,//;
+ #French accents substitution
+ $origtext =~ s/&agrave;/à/;
+ $origtext =~ s/&eacute;/é/;
+
+ return if ($self->{parsingEnded});
+
+ if ($self->{parsingList})
+ {
+
+ }
+ else
+ {
+ # fetching information from page
+ if ($origtext =~ /^Nom VO/)
+ {
+ $self->{curInfo}->{original} = $origtext;
+ $self->{curInfo}->{original} =~ s/Nom VO : //;
+ }
+ if ($self->{insideName})
+ {
+ $self->{curInfo}->{name} = $origtext;
+ $self->{insideName} = 0;
+ }
+ elsif ($self->{insideYear})
+ {
+ $self->{curInfo}->{released} = $origtext;
+ $self->{curInfo}->{released} =~ s/([^0-9])//g;
+ $self->{insideYear} = 0;
+ }
+ elsif ($self->{insideDesigner})
+ {
+ # Append text (and trailing ,) to existing designer field
+ $self->{curInfo}->{designedby} .= $origtext.", ";
+ $self->{insideDesigner} = 0;
+ }
+ elsif ($self->{insideIllustrator})
+ {
+ # Append text (and trailing ,) to existing designer field
+ $self->{curInfo}->{illustratedby} .= $origtext.", ";
+ $self->{insideIllustrator} = 0;
+ }
+ elsif ($self->{insidePublishers})
+ {
+ $self->{curInfo}->{publishedby} = $origtext;
+ $self->{insidePublishers} = 0;
+ }
+ elsif ($self->{insidePlayers})
+ {
+ $self->{curInfo}->{players} = $origtext;
+ $self->{insidePlayers} = 0;
+ }
+ elsif ($self->{insideAges})
+ {
+ $self->{curInfo}->{suggestedage} = $origtext;
+ $self->{insideAges} = 0;
+ }
+ elsif ($self->{insidePlayingTime})
+ {
+ $self->{curInfo}->{playingtime} = $origtext;
+ $self->{insidePlayingTime} = 0;
+ }
+ elsif ($self->{insideExpands})
+ {
+ $self->{curInfo}->{expansionfor} .= $origtext;
+
+ }
+ elsif ($self->{insideExpansion})
+ {
+ $self->{curInfo}->{expandedby} .= $self->capWord($origtext).',';
+ $self->{insideExpansion} = 0;
+ }
+ elsif ($self->{insideDescription})
+ {
+ $self->{curInfo}->{description} .= $origtext;
+ }
+ elsif ($self->{insideMechanic})
+ {
+ $self->{curInfo}->{mechanics} .= $self->capWord($origtext).',';
+ $self->{insideMechanic} = 0;
+ }
+ elsif ($self->{insideCategory})
+ {
+ $self->{curInfo}->{category} .= $self->capWord($origtext).',';
+ $self->{insideCategory} = 0;
+ }
+
+
+ # Pre-detection based on text (not tags) for various fields
+ # that have no specific id in tags
+ if ($origtext =~ /^Ann\xe9e/)
+ {
+ $self->{nextIsYear} = 1;
+ }
+ if ($origtext =~ /^Auteur/)
+ {
+ $self->{insideDesignerRow} = 1;
+ }
+ if ($origtext =~ /^Illustrateur/)
+ {
+ $self->{insideIllustratorRow} = 1;
+ }
+ if ($origtext =~ /^Editeur/)
+ {
+ $self->{nextIsPublishers} = 1;
+ }
+ if ($origtext =~ /^Joueurs/)
+ {
+ $self->{nextIsPlayers} = 1;
+ }
+ if ($origtext =~ /^Age/)
+ {
+ $self->{nextIsAges} = 1;
+ }
+ if ($origtext =~ /^Dur/)
+ {
+ $self->{nextIsPlayingTime} = 1;
+ }
+ if ($origtext =~ /^Ceci est une extension pour/)
+ {
+ $self->{insideExpands} = 1;
+ }
+ if ($origtext =~ /canisme\(s\)/)
+ {
+ $self->{insideMechanicRow} = 1;
+ }
+ if ($origtext =~ /Th.{1,8}me\(s\)/)
+ {
+ $self->{insideCategoryRow} = 1;
+ }
+ if ($origtext =~ /^Les extensions/)
+ {
+ $self->{insideExpansionList} = 1;
+ }
+
+ }
+ }
+
+ sub new
+ {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new();
+ bless ($self, $class);
+
+ $self->{hasField} = {
+ name => 1,
+ released => 1,
+ };
+
+ $self->{isBoardgame} = 0;
+ $self->{curName} = undef;
+ $self->{curUrl} = undef;
+
+ return $self;
+ }
+
+ sub preProcess
+ {
+ my ($self, $html) = @_;
+
+ $self->{parsingEnded} = 0;
+
+ $html =~ s/"&#34;/'"/g;
+ $html =~ s/&#34;"/"'/g;
+ $html =~ s|</a></b><br>|</a><br>|;
+
+ $html =~ s|\x{92}|'|gi;
+ $html =~ s|&#146;|'|gi;
+ $html =~ s|&#149;|*|gi;
+ $html =~ s|&#133;|...|gi;
+ $html =~ s|\x{85}|...|gi;
+ $html =~ s|\x{8C}|OE|gi;
+ $html =~ s|\x{9C}|oe|gi;
+
+ return $html;
+ }
+
+ sub getSearchUrl
+ {
+ my ($self, $word) = @_;
+
+ # Url returned below is the for the search page, where $word is replaced by the search
+ return "http://trictrac.net/index.php3?id=jeux&rub=ludotheque&inf=cat&choix=$word";
+ }
+
+ sub getItemUrl
+ {
+ my ($self, $url) = @_;
+
+ return $url if $url =~ /^http:/;
+ if ($url =~ /^\//)
+ {
+ return "http://trictrac.net".$url;
+ }
+ else
+ {
+ return "http://trictrac.net/".$url;
+ }
+ }
+
+ sub getName
+ {
+ return "Tric Trac";
+ }
+
+ sub getAuthor
+ {
+ return 'Florent';
+ }
+
+ sub getLang
+ {
+ return 'FR';
+ }
+
+
+}
+
+1;