1package Yahoo::Search::Result;
2use strict;
3
4our $VERSION = "20100614.1"; # just to make CPAN happy
5
6=head1 NAME
7
8
9=head1 VERSION
10
11version 1.11.3
12Yahoo::Search::Result -- class representing a single result (single web
13page, image, video file, etc) from a Yahoo! search-engine query.
14(This package is included in, and automatically loaded by, the Yahoo::Search package.)
15
16=head1 Package Use
17
18You never need to C<use> this package directly -- it is loaded
19automatically by Yahoo::Search.
20
21=head1 Object Creation
22
23C<Result> objects are created automatically when a C<Response> object is
24created (when a C<Request> object's C<Fetch> method is called, either
25directly, or indirectly via a shortcut such as
26C<Yahoo::Search-E<gt>Query()>.
27
28=head1 Methods Overview
29
30This table shows the methods available on a per-search-space basis:
31
32
33
34                                                     Terms
35                                              Related  |
36                                           Spell  |    |
37                                      Local  |    |    |
38                                  News  |    |    |    |
39                            Video  |    |    |    |    |
40                       Image  |    |    |    |    |    |
41                   Doc   |    |    |    |    |    |    |
42                    |    |    |    |    |    |    |    |
43   Next            [X]  [X]  [X]  [X]  [X]  [X]  [X]  [X]
44   Prev            [X]  [X]  [X]  [X]  [X]  [X]  [X]  [X]
45   Response        [X]  [X]  [X]  [X]  [X]  [X]  [X]  [X]
46   Request         [X]  [X]  [X]  [X]  [X]  [X]  [X]  [X]
47   SearchSpace     [X]  [X]  [X]  [X]  [X]  [X]  [X]  [X]
48
49 * I               [X]  [X]  [X]  [X]  [X]  [X]  [X]   .
50 * i               [X]  [X]  [X]  [X]  [X]  [X]  [X]   .
51   as_html         [X]  [X]  [X]  [X]  [X]  [X]  [X]   .
52   as_string       [X]  [X]  [X]  [X]  [X]  [X]  [X]   .
53   Data            [X]  [X]  [X]  [X]  [X]  [X]  [X]   .
54
55 * Url             [X]  [X]  [X]  [X]  [X]   .    .    .
56 * ClickUrl        [X]  [X]  [X]  [X]  [X]   .    .    .
57 * Title           [X]  [X]  [X]  [X]  [X]   .    .    .
58   TitleAsHtml     [X]  [X]  [X]  [X]  [X]   .    .    .
59   Link            [X]  [X]  [X]  [X]  [X]   .    .    .
60 * Summary         [X]  [X]  [X]  [X]   .    .    .    .
61   SummaryAsHtml   [X]  [X]  [X]  [X]   .    .    .    .
62
63 * CacheUrl        [X]   .    .    .    .    .    .    .
64 * CacheSize       [X]   .    .    .    .    .    .    .
65 * ModTimestamp    [X]   .    .   [X]   .    .    .    .
66
67 * Width            .   [X]  [X]   .    .    .    .    .
68 * Height           .   [X]  [X]   .    .    .    .    .
69
70 * ThumbUrl         .   [X]  [X]  [X]   .    .    .    .
71 * ThumbWidth       .   [X]  [X]  [X]   .    .    .    .
72 * ThumbHeight      .   [X]  [X]  [X]   .    .    .    .
73   ThumbImg         .   [X]  [X]  [X]   .    .    .    .
74   ThumbLink        .   [X]  [X]  [X]   .    .    .    .
75
76 * HostUrl          .   [X]  [X]   .    .    .    .    .
77 * Copyright        .   [X]  [X]   .    .    .    .    .
78 * Publisher        .   [X]  [X]   .    .    .    .    .
79 * Restrictions     .   [X]  [X]   .    .    .    .    .
80
81 * Type            [X]  [X]  [X]   .    .    .    .    .
82 * Bytes            .   [X]  [X]   .    .    .    .    .
83 * Channels         .    .   [X]   .    .    .    .    .
84 * Seconds          .    .   [X]   .    .    .    .    .
85 * Duration         .    .   [X]   .    .    .    .    .
86 * Streaming        .    .   [X]   .    .    .    .    .
87
88 * SourceName       .    .    .   [X]   .    .    .    .
89   SourceNameAsHtml .    .    .   [X]   .    .    .    .
90 * SourceUrl        .    .    .   [X]   .    .    .    .
91 * Language         .    .    .   [X]   .    .    .    .
92 * PublishTime      .    .    .   [X]   .    .    .    .
93 * PublishWhen      .    .    .   [X]   .    .    .    .
94
95 * Address          .    .    .    .   [X]   .    .    .
96 * City             .    .    .    .   [X]   .    .    .
97 * State            .    .    .    .   [X]   .    .    .
98 * Phone            .    .    .    .   [X]   .    .    .
99 * Miles            .    .    .    .   [X]   .    .    .
100 * Kilometers       .    .    .    .   [X]   .    .    .
101 * Rating           .    .    .    .   [X]   .    .    .
102 * MapUrl           .    .    .    .   [X]   .    .    .
103 * BusinessUrl      .    .    .    .   [X]   .    .    .
104 * BusinessClickUrl .    .    .    .   [X]   .    .    .
105 * AllMapUrl        .    .    .    .   [X]   .    .    .
106
107 * Term             .    .    .    .    .   [X]  [X]  [X]
108   TermAsHtml       .    .    .    .    .   [X]  [X]  [X]
109
110                    |    |    |    |    |    |    |    |
111                   Doc   |    |    |    |    |    |    |
112                       Image  |    |    |    |    |    |
113                            Video  |    |    |    |    |
114                                  News  |    |    |    |
115                                      Local  |    |    |
116                                           Spell  |    |
117                                              Related  |
118                                                     Terms
119
120
121Those items marked with a '*' are also available via the C<Data> method
122
123=cut '
124
125
126
127my @DOW = qw[x Sun Mon Tue Wed Thu Fri Sat];
128my @MON = qw[x Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec];
129
130## helper function -- returns the given text cooked for html
131sub _cook_for_html($)
132{
133    my $text = shift;
134
135    #die join(',', caller) if not defined $text;
136
137    $text =~ s/&/&amp;/g;
138    $text =~ s/</&lt;/g;
139    $text =~ s/>/&gt;/g;
140    return $text;
141}
142
143##
144## helper function -- given a key in a result object, a result object (the
145## "self" from a method), and an indication of whether we want text or
146## html, return the appropriate text or html.
147##
148sub _text_or_html($@)
149{
150    my $Key    = shift;
151    my $Result = shift;
152    my $AsHtml = shift; #optional
153
154    my $Text = $Result->{$Key};
155
156    if (not defined $Text) {
157        return ();
158    } elsif ($AsHtml) {
159        return _cook_for_html($Text);
160    } else {
161        return $Text;
162    }
163}
164
165
166##
167## helper function -- if passed one arg, it's a url, and simply return it.
168##
169## If passed multiple args, the 2nd is an attribute (e.g. "href", "src"),
170## which causes the return of a string like
171##   href="$url"
172## where we're sure the quoting of the url is safe.
173##
174sub _url($@)
175{
176    my $Url = shift;
177    my $Attrib = shift;
178
179    if (not $Url)
180    {
181        return ();
182    }
183    elsif (not $Attrib)
184    {
185        return $Url;
186    }
187    elsif (not $Url =~ m/\x22/) {
188        return qq/$Attrib="$Url"/;
189    } elsif (not $Url =~ m/\x27/) {
190        return qq/$Attrib='$Url'/;
191    } else {
192        $Url =~ s/\x22/%22/g; # double qoute
193        $Url =~ s/\x27/%27/g; # single quote
194        return qq/$Attrib="$Url"/;
195    }
196}
197
198
199##
200## Want to be able to dump a hash of most data, so note which items are
201## available and interesting on a per-search-space basis.
202##
203my @CommonItems = qw[Url ClickUrl Summary Title i I];
204
205my %ItemsBySpace =
206(
207 Video   => [@CommonItems, qw"Type Bytes HostUrl Copyright Publisher Restrictions Channels Seconds Duration Streaming Width Height ThumbUrl ThumbWidth ThumbHeight"],
208 Image   => [@CommonItems, qw"Type Bytes HostUrl Copyright Publisher Restrictions                                     Width Height ThumbUrl ThumbWidth ThumbHeight"],
209 Doc     => [@CommonItems, qw"Type CacheUrl CacheSize ModTimestamp"],
210 Local   => [@CommonItems, qw"Address City State Phone Miles Kilometers Rating MapUrl AllMapUrl"],
211 News    => [@CommonItems, qw"SourceName SourceUrl Language ModTimestamp PublishTime ThumbUrl ThumbWidth ThumbHeight"],
212 Spell   => [@CommonItems,   "Term"],
213 Related => [@CommonItems,   "Term"],
214 Terms   => [@CommonItems,   "Term"],
215);
216
217
218
219
220=head1 METHODS
221
222=over 4
223
224=cut
225
226##############################################################################
227
228=item $Result->Next([I<boolean>])
229
230Returns the next C<Result> object from among the list of result objects
231that are part of one C<Response> object.
232
233Returns nothing when called on the last result in a response, unless
234auto-continuation is turned on, in which case the next set is automatically
235fetched and the first C<Result> from that set's C<Response> is returned.
236
237An optional defined boolean argument turns auto-continuation on (true) or
238off (false). If the argument is not defined, or not provided, the value for
239the original request's C<AutoContinue> option (default off) is used.
240
241Note that using auto-continuation can be dangerous. See the docs for
242C<NextResult> in Yahoo::Search::Response.
243
244=cut
245
246sub Next
247{
248    my $Result = shift; # self
249    my $AutoContinue = shift;
250
251    if ($Result->{_ResponseOrdinal} < $#{ $Result->{_Response}->{Result} })
252    {
253        return $Result->{_Response}->{Result}->[$Result->{_ResponseOrdinal} + 1];
254    }
255    else
256    {
257        if (not defined $AutoContinue) {
258            $AutoContinue = $Result->{_Response}->{_Request}->{AutoContinue};
259        }
260
261        if ($AutoContinue
262            and
263            my $NextResponse = $Result->{_Response}->NextSet)
264        {
265            return $NextResponse->NextResult();
266        }
267        else
268        {
269            return ()
270        }
271    }
272}
273
274
275
276
277##############################################################################
278
279=item $Result->Prev
280
281The opposite of C<Next>. No auto-continuation feature.
282
283=cut
284
285## does not auto-fetch when fetching result[-1]
286sub Prev
287{
288    my $Result = shift; # self
289
290    if ($Result->{_ResponseOrdinal} == 0) {
291        return ();
292    } else {
293        return $Result->{_Response}->{Result}->[$Result->{_ResponseOrdinal} - 1];
294    }
295}
296
297
298
299##############################################################################
300
301=item $Result->Response
302
303Returns the C<Response> object of which this C<Result> object is a part.
304
305=cut
306
307sub Response
308{
309    my $Result = shift; # self
310    return $Result->{_Response};
311}
312
313
314
315##############################################################################
316
317=item $Result->Request
318
319Returns the original C<Request> object from which this C<Result> object's
320C<Response> was derived.
321
322=cut '
323
324sub Request
325{
326    my $Result = shift; # self
327    return $Result->{_Response}->{_Request};
328}
329
330
331
332
333##############################################################################
334
335=item $Result->SearchSpace
336
337Returns a string which indicates the search space of the original query
338that this result was part of. (That is, it returns C<Doc>, C<Image>,
339C<News>, C<Local>, or C<Video>.)
340
341It's the same as
342
343   $Result->Request->SearchSpace;
344
345=cut
346
347sub SearchSpace
348{
349    my $Result = shift; # self
350    return $Result->{_Response}->{_Request}->{Space};
351}
352
353
354
355##############################################################################
356
357
358=item $Result->i[ I<separator> ]
359
360=item $Result->I[ I<separator> ]
361
362The first (lower-case letter "i") returns the zero-based ordinal of the
363result from among those in the current C<Response>.
364
365The latter (upper-case letter "I") returns the zero-based ordinal of the
366result from among all search results that might be returned by Yahoo! for
367the given query.
368
369For example, after
370
371  my @Results = Yahoo::Search->Results(Image => "Briteny",
372                                       AppId => "my app id",
373                                       Start => 45,
374                                       Count => 15);
375
376the C<$Results[0]> result object has an C<I> of 45 (the 45th result of all
377"Briteny" image results) and an C<i> of 0 (the 0th result among those
378returned this time.)
379
380In either case, if an optional argument is given and is true, it is used as
381a separator every three digits. In the US, one would use
382
383    $Result->I(',')
384
385to return "1,234" where
386
387    $Result->I()
388
389would return "1234".
390
391=cut
392
393
394sub i
395{
396    my $Result = shift; # self
397    my $Comma = shift; # optional
398    return Yahoo::Search::Response::_commaize($Result->{_ResponseOrdinal}, $Comma);
399}
400
401sub I
402{
403    my $Result = shift; # self
404    my $Comma = shift; # optional
405    return Yahoo::Search::Response::_commaize($Result->{_ResponseOrdinal} + $Result->{_Response}->{firstResultPosition} - 1, $Comma);
406}
407
408
409
410
411##############################################################################
412
413=item $Result->as_html
414
415Returns a string of HTML that represents the result, as appropriate to the
416result's query search space.
417
418There are many ways one may wish to display query results -- this method
419returns one display that the author finds useful. It may come in useful for
420quick prototyping of web applications, e.g.
421
422  sub ShowRelated
423  {
424    print join "<hr>", map { $_->as_html } Yahoo::Search->Results(@_);
425  }
426
427(Also see C<Yahoo::Search-E<gt>HtmlResults>)
428
429The HTML returned by C<as_html> contains class references, thereby allowing
430the look-and-feel to be easily adjusted. Here's a style sheet that makes
431Image search results look palatable.
432
433  <style>
434    .yResult { display: block; border: #CCF 3px solid ; padding:10px }
435    .yLink   { }
436    .yTitle  { display:none }
437    .yImg    { border: solid 1px }
438    .yUrl    { display:none }
439    .yMeta   { font-size: 80% }
440    .ySrcUrl { }
441    .ySum    { font-family: arial; font-size: 90% }
442  </style>
443
444B<Bugs>: English-centric
445
446=cut '
447
448sub as_html
449{
450    my $Result = shift; # self
451    my $SearchSpace = $Result->SearchSpace;
452    my $summary = $Result->Summary(1);
453
454    if ($SearchSpace eq 'Doc')
455    {
456        my $link = $Result->Link;
457        my $url  = $Result->Url;
458
459        my $html = "$link<span class=yUrl><br>$url</span>";
460        if ($summary) {
461            $html .= "<span class=ySum><br>$summary</span>";
462        }
463        return "<span class=yResult>$html</span>";
464    }
465
466    if ($SearchSpace eq 'Video')
467    {
468        my $HREF   = $Result->ClickUrl('HREF');
469        my $title = $Result->Title(1);
470        my $html;
471        if (my $img = $Result->ThumbImg) {
472            $html = "<a class=yLink $HREF>$img<span class=yTitle> $title</span></a>";
473        } else {
474            $html = "<a class=yLink $HREF><span class=yTitle>$title</span></a>";
475        }
476
477        $html .= "<span class=yUrl><br>" . $Result->Url . "</span>";
478
479        my @extra;
480        if (my $duration = $Result->Duration) {
481            push @extra, "Duration: $duration";
482        }
483
484        if (my $width = $Result->Width and my $height = $Result->Height) {
485            push @extra, "Video resolution $width x $height";
486        }
487
488        if (my $size = $Result->Bytes) {
489            push @extra, "File size: $size";
490        }
491
492        if (my $chan = $Result->Channels) {
493            push @extra, "$chan-channel audio";
494        }
495
496        if (my $HREF = $Result->HostUrl('href')) {
497            push @extra, "<a class=ySrcUrl $HREF>Source page</a>";
498        }
499        if (@extra) {
500            $html .= "<span class=yMeta><br>" . join(" | ", @extra) . "</span>";
501        }
502
503        if ($summary) {
504            $html .= "<span class=ySum><br>$summary</span>";
505        }
506        return "<span class=yResult>$html</span>";
507    }
508
509    if ($SearchSpace eq 'Image')
510    {
511        my $HREF  = $Result->ClickUrl('href');
512        my $title = $Result->Title(1);
513        my $html;
514        if (my $img = $Result->ThumbImg) {
515            $html = "<a class=yLink $HREF>$img<span class=yTitle> $title</span></a>";
516        } else {
517            $html = "<a class=yLink $HREF><span class=yTitle>$title</span></a>";
518        }
519
520        $html .= "<span class=yUrl><br>" . $Result->Url . "</span>";
521
522        my @extra;
523        if (my $size = $Result->Bytes) {
524            push @extra, "File size: $size";
525        }
526        if (my $width = $Result->Width and my $height = $Result->Height) {
527            push @extra, "Image size: $width x $height";
528        }
529        if (my $HREF = $Result->HostUrl('HREF')) {
530            push @extra, "<a class=ySrcUrl $HREF>Source page</a>";
531        }
532        if (@extra) {
533            $html .= "<span class=yMeta><br>" . join(" | ", @extra) . "</span>";
534        }
535
536        if ($summary) {
537            $html .= "<span class=ySum><br>$summary</span>";
538        }
539        return "<span class=yResult>$html</span>";
540    }
541
542    if ($SearchSpace eq "News")
543    {
544        my $HREF  = $Result->ClickUrl('HREF');
545        my $title = $Result->Title(1);
546        my $html  = "<span class=yResult>";
547        if (my $img = $Result->ThumbImg) {
548            $html .= "<a class=yLink $HREF>$img<span class=yTitle> $title</span></a>";
549        } else {
550            $html .= "<a class=yLink $HREF><span class=yTitle>$title</span></a>";
551        }
552        my $src_name = $Result->SourceNameAsHtml;
553        my $src_href = $Result->SourceUrl('HREF');
554        if ($src_name and $src_href) {
555            $html .= "<a class=yNewsSrc $src_href><br>" . _cook_for_html($src_name) . "</a>";
556        }
557        if (my $when = $Result->PublishWhen) {
558            $html .= " <span class=yWhen>($when)</span>";
559        }
560
561        if ($summary) {
562            $html .= "<span class=ySum><br>$summary</span>";
563        }
564        return "<span class=yResult>$html</span>";
565    }
566
567    if ($SearchSpace eq "Local")
568    {
569        my $html = $Result->Link;
570
571        if (my $addr = join(', ', grep { $_ } $Result->Address, $Result->City . " " . $Result->State)) {
572            $html .= "<span class=yAddr><br>$addr</span>";
573        }
574
575        my @extra;
576        if (my $phone = $Result->Phone) {
577            push @extra, "<span class=yPhone>$phone</span>";
578        }
579        if (my $HREF = $Result->MapUrl('href')) {
580            push @extra, "<a class=yMap $HREF>Map</a>";
581        }
582        if (@extra) {
583            $html .= "<span class=yMeta><br>" . join(" | ",@extra) . "</span>";
584        }
585
586        if ($summary) {
587            $html .= "<span class=ySum><br>$summary</span>";
588        }
589        return "<span class=yResult>$html</span>";
590    }
591
592    if ($SearchSpace eq "Spell")
593    {
594        my $item = $Result->TermAsHtml;
595        return "Did you mean <i>$item</i>?";
596    }
597
598    if ($SearchSpace eq "Related")
599    {
600        my $item = $Result->TermAsHtml;
601        return "Also try: <i>$item</i>";
602    }
603
604    if ($SearchSpace eq "Terms")
605    {
606        my $item = $Result->TermAsHtml;
607        return "Term: <i>$item</i>";
608    }
609
610    return "???";
611}
612
613
614##############################################################################
615
616=item $Result->as_string
617
618Returns a textual representation of the C<Result>, which may be useful for
619quick prototyping or debugging.
620
621=cut
622
623
624## must create, for all spaces
625sub as_string
626{
627    my $Result = shift; # self
628    my $ref = $Result->Data;
629
630    my $txt = "";
631
632    for my $item (@{$ItemsBySpace{$Result->SearchSpace}})
633    {
634        if (defined(my $val = $Result->$item)) {
635            $txt .= "$item: $val\n";
636        }
637    }
638    return $txt;
639}
640
641##############################################################################
642
643=item $Result->Data
644
645Returns a list of key/value pairs containing the fundamental data for the
646result (those items marked with '*' in the table at the start of this
647document).
648
649  my %Data = $Result->Data;
650
651=cut
652
653
654sub Data
655{
656    my $Result = shift; # self
657    my %Data;
658
659    for my $item (@{$ItemsBySpace{$Result->SearchSpace}})
660    {
661        $Data{$item} = $Result->$item;
662    }
663    return %Data;
664}
665
666
667
668##############################################################################
669
670=item $Result->Url
671
672=item $Result->ClickUrl
673
674C<Url> returns the raw url of the item (web page, image, etc.), appropriate
675for display to the user.
676
677C<ClickUrl> returns a url appropriate for the href attribute of a link.
678
679In some cases, the two return the same url.
680
681As with all Result-object methods which return a url of some sort, you can
682provide a single argument such as C<href> and receive a string such as
683   href="..."
684appropriate to be used directly in html. For example,
685
686   my $HREF = $Result->ClickUrl('href');
687   print "<a $HREF>click</a>";
688
689is preferable to
690
691   my $url = $Result->ClickUrl;
692   print "<a href='$url'>click</a>";
693
694since the latter would break if C<$url> contains a singlequote.
695
696=cut
697
698sub Url
699{
700    my $Result = shift; # self
701    return _url($Result->{Url} || $Result->{ClickUrl}, @_);
702}
703
704sub ClickUrl
705{
706    my $Result = shift; # self
707    return _url($Result->{ClickUrl} || $Result->{Url}, @_);
708}
709
710
711
712
713
714##############################################################################
715
716=item $Result->Title([ I<as_html> ])
717
718=item $Result->TitleAsHtml
719
720C<Title> returns the raw title text associated with the result. If an
721optional argument is provided and is true, the title text is returned as
722html.
723
724C<TitleAsHtml> is the same as
725
726  $Result->Title(1)
727
728=cut
729
730sub Title
731{
732    return _text_or_html(Title => @_);
733}
734
735sub TitleAsHtml
736{
737    my $Result = shift; #self
738    return $Result->Title(1);
739}
740
741
742
743
744##############################################################################
745
746=item $Result->Link
747
748Returns a link made from the C<ClickUrl> and the C<Title>, with class
749"yLink", e.g.
750
751   <a class=yLink href='$URL'>$TITLE</a>
752
753=cut
754
755sub Link
756{
757    my $Result = shift; # self
758
759    if (my $HREF = $Result->ClickUrl('href')
760        and
761        my $title = $Result->Title(1))
762    {
763        return "<a class=yLink $HREF>$title</a>";
764    }
765    else
766    {
767        return ();
768    }
769}
770
771
772
773##############################################################################
774
775=item $Result->Summary([ I<as_html> ])
776
777=item $Result->SummaryAsHtml
778
779Like C<Title> and C<TitleAsHtml>, but for the summary associated with the
780result.
781
782=cut
783
784sub Summary
785{
786    return _text_or_html(Summary => @_);
787}
788
789sub SummaryAsHtml
790{
791    my $Result = shift; #self
792    return $Result->Summary(1);
793}
794
795
796=item $Result->CacheUrl
797
798=item $Result->CacheSize
799
800(I<Appropriate for B<Doc> search results>)
801
802C<CacheUrl> returns the url of the document in the Yahoo! cache.
803See the documentation for the C<Url> method for information on the
804one-argument version of this method.
805
806C<CacheSize> returns the size (as a string like "22k").
807
808=cut
809
810sub CacheUrl
811{
812    my $Result = shift; # self
813    return _url($Result->{Cache} ? $Result->{Cache}->{Url}  : (), @_)
814}
815
816sub CacheSize
817{
818    my $Result = shift; # self
819    return $Result->{Cache} ? $Result->{Cache}->{Size} : ();
820}
821
822
823
824##############################################################################
825
826=item $Result->ModTimestamp
827
828(I<Appropriate for B<Doc> and B<News> search results>)
829
830The Unix timestamp of the Last-Modified time associated with the the url
831when it was last checked by Yahoo!'s backend crawlers.
832
833=cut
834
835sub ModTimestamp
836{
837    my $Result = shift; # self
838    return defined($Result->{ModificationDate}) ? $Result->{ModificationDate}: ();
839}
840
841
842##############################################################################
843
844=item $Result->Width
845
846=item $Result->Height
847
848(I<Appropriate for B<Image> and B<Video> search results>)
849
850The width and height (in pixels) of the image or video.
851
852=cut
853
854## for image, video
855sub Width
856{
857    my $Result = shift; # self
858    return defined($Result->{Width}) ? $Result->{Width} : ();
859}
860
861sub Height
862{
863    my $Result = shift; # self
864    return defined($Result->{Height}) ? $Result->{Height} : ();
865}
866
867
868
869##############################################################################
870
871=item $Result->ThumbUrl
872
873=item $Result->ThumbWidth
874
875=item $Result->ThumbHeight
876
877(I<Appropriate for B<Image>, B<Video>, and B<News> search results>)
878
879The url of a thumbnail image, and its width and height.
880
881(Note: few I<News> results have a thumbnail, but some do.)
882
883See the documentation for the C<Url> method for information on the
884one-argument version of C<ThumbUrl>.
885
886=cut
887
888sub ThumbUrl
889{
890    my $Result = shift; # self
891    return _url($Result->{Thumbnail} ? $Result->{Thumbnail}->{Url}    : (), @_);
892}
893
894sub ThumbWidth
895{
896    my $Result = shift; # self
897    return $Result->{Thumbnail} ? $Result->{Thumbnail}->{Width}  : ();
898}
899
900sub ThumbHeight
901{
902    my $Result = shift; # self
903    return $Result->{Thumbnail} ? $Result->{Thumbnail}->{Height} : ();
904}
905
906
907##############################################################################
908
909=item $Result->ThumbImg
910
911(I<Appropriate for B<Image>, B<Video>, and B<News> search results>)
912
913Returns a C<E<lt>imgE<gt>> tag representing the thumbnail image, e.g.
914
915  <img class=yImg src='$IMGURL' width=$WIDTH height=$HEIGHT>
916
917=cut
918
919
920sub ThumbImg
921{
922    my $Result = shift; # self
923
924    my $SRC    = $Result->ThumbUrl('src');
925    my $Width  = $Result->ThumbWidth;
926    my $Height = $Result->ThumbHeight;
927
928    if ($SRC) {
929        return "<img class=yImg $SRC width=$Width height=$Height>";
930    } else {
931        return ();
932    }
933}
934
935
936##############################################################################
937
938=item $Result->ThumbLink
939
940(I<Appropriate for B<Image>, B<Video>, and B<News> search results>)
941
942Returns a link from the thumbnail to the C<ClickUrl> of the result,
943e.g.
944
945  <a class=yLink href='$CLICKURL'>
946    <img class=yImg src='$IMGURL' width=$WIDTH height=$HEIGHT>
947  </a>
948
949=cut
950
951
952sub ThumbLink
953{
954    my $Result = shift; # self
955    my $HREF = $Result->ClickUrl('href');
956    my $img  = $Result->ThumbImg;
957    if ($HREF and $img) {
958        return "<a class=yLink $HREF>$img</a>";
959    } else {
960        return ();
961    }
962}
963
964
965
966##############################################################################
967
968=item $Result->HostUrl
969
970(I<Appropriate for B<Image> and B<Video> search results>)
971
972Returns the url of the web page containing a link to the image/video
973item that the C<Result> represents.
974
975See the documentation for the C<Url> method for information on the
976one-argument version of this method.
977
978=cut
979
980sub HostUrl
981{
982    my $Result = shift; # self
983    return _url($Result->{RefererUrl}, @_);
984}
985
986=cut
987
988
989
990###########################################################################
991
992=item $Result->Type
993
994(<Appropriate for B<Doc>, B<Image>, and B<Video> search results>)
995
996Returns a string representing the file type of the item to which
997C<$Result-E<gt>Url> points. For I<Doc> searches, the MIME type (e.g.
998"text/html") is returned.
999
1000For other search spaces, here are the possible return values:
1001
1002  Video:  avi  flash  mpeg  msmedia  quicktime  realmedia
1003  Image:  bmp  gif  jpg  png.
1004
1005Yahoo! Search derives these Video/Image C<Type> value by actually
1006inspecting the file contents, and as such it is more reliable than looking
1007at the file extension.
1008
1009=cut
1010
1011sub Type
1012{
1013    my $Result = shift; #self
1014    if (defined $Result->{MimeType}) {
1015        return $Result->{MimeType};
1016    } elsif (defined $Result->{FileFormat}) {
1017        return $Result->{FileFormat};
1018    } else {
1019        return ();
1020    }
1021}
1022
1023
1024
1025###########################################################################
1026
1027=item $Result->Copyright([ I<as_html> ])
1028
1029(<Appropriate for B<Image> and B<Video> search results>)
1030
1031Returns any copyright notice associated with the result. If an optional
1032argument is provided and is true, the copyright text is returned as html.
1033
1034=cut
1035
1036sub Copyright
1037{
1038    return _text_or_html(Copyright => @_);
1039}
1040
1041
1042
1043###########################################################################
1044
1045=item $Result->Publisher([ I<as_html> ])
1046
1047(<Appropriate for B<Image>, and B<Video> search results>)
1048
1049Returns any publisher information (as a string) associated with the result.
1050If an optional argument is provided and is true, the publisher information
1051is returned as html.
1052
1053=cut
1054
1055sub Publisher
1056{
1057    return _text_or_html(Publisher => @_);
1058}
1059
1060
1061
1062###########################################################################
1063
1064=item $Result->Restrictions
1065
1066(<Appropriate for B<Image>, and B<Video> search results>)
1067
1068A (possibly zero-length) string containing zero or more of the following
1069space-separated words:
1070
1071  noframe
1072  noinline
1073
1074See Yahoo!'s web site (http://developer.yahoo.net/) for information on them.
1075
1076=cut
1077
1078sub Restrictions
1079{
1080    my $Result = shift; #self
1081    if (not defined $Result->{Restrictions}) {
1082        return "";
1083    } else {
1084        return $Result->{Restrictions};
1085    }
1086}
1087
1088
1089
1090##############################################################################
1091
1092=item $Result->Bytes
1093
1094(I<Appropriate for B<Image>, and B<Video> search results>)
1095
1096The size of the image/video item, in bytes.
1097
1098=cut
1099
1100sub Bytes
1101{
1102    my $Result = shift; #self
1103
1104    if ($Result->{FileSize}) {
1105        return $Result->{FileSize};
1106    } else {
1107        return ();
1108    }
1109}
1110
1111
1112
1113
1114##############################################################################
1115
1116=item $Result->Channels
1117
1118(I<Appropriate for B<Video> search results>)
1119
1120Returns the number of channels in the audio, if known.
1121Examples are "1", "2", "4.1", "5.1", etc....
1122
1123=cut
1124
1125sub Channels
1126{
1127    my $Result = shift; # self
1128    if ($Result->{Channels}) {
1129        return $Result->{Channels};
1130    } else {
1131        return ();
1132    }
1133}
1134
1135
1136
1137##############################################################################
1138
1139=item $Result->Seconds
1140
1141(I<Appropriate for B<Video> search results>)
1142
1143Returns the duration of the video clip, if known, in (possibly fractional)
1144seconds.
1145
1146=cut
1147
1148sub Seconds
1149{
1150    my $Result = shift; #self
1151
1152    if ($Result->{Duration}) {
1153        return $Result->{Duration};
1154    }
1155    return ();
1156}
1157
1158
1159
1160##############################################################################
1161
1162=item $Result->Duration
1163
1164(I<Appropriate for B<Video> search results>)
1165
1166Returns a string representing the duration of the video clip, if known, in
1167the form of "37 sec", "1:23", or "4:56:23", as appropriate.
1168
1169B<Bugs>: English-centric
1170
1171=cut
1172
1173sub Duration
1174{
1175    my $Result = shift; #self
1176
1177    if (my $sec = $Result->Seconds)
1178    {
1179        if ($sec < 60) {
1180            return sprintf "%d sec", $sec;
1181        }
1182        if ($sec < 3600) {
1183            return sprintf "%d:%02d", int($sec/60), $sec%60;
1184        }
1185        my $hours = int($sec/3600);
1186        $sec = $sec % 3600;
1187        return sprintf "%d:%02d:%02d", $hours, int($sec/60), $sec%60;
1188    }
1189
1190    return ();
1191}
1192
1193
1194
1195##############################################################################
1196
1197=item $Result->Streaming
1198
1199(I<Appropriate for B<Video> search results>)
1200
1201Returns "1" if the multimedia is streaming, "0" if not.
1202If not known, an empty list is returned.
1203
1204=cut
1205
1206sub Streaming
1207{
1208    my $Result = shift; #self
1209
1210    my $Stream = $Result->{Streaming} || '';
1211    if ($Stream eq 'true') {
1212        return 1;
1213    } elsif  ($Stream eq 'false') {
1214        return 0;
1215    } else {
1216        return ();
1217    }
1218}
1219
1220
1221
1222##############################################################################
1223
1224=item $Result->SourceUrl
1225
1226(I<Appropriate for B<News> search results>)
1227
1228The main url of the news provider hosting the article that the C<Result>
1229refers to.
1230
1231See the documentation for the C<Url> method for information on the
1232one-argument version of this method.
1233
1234=cut
1235
1236sub SourceUrl
1237{
1238    my $Result = shift; # self
1239    return _url($Result->{NewsSourceUrl}, @_);
1240}
1241
1242
1243
1244
1245##############################################################################
1246
1247=item $Result->SourceName([ I<as_html> ])
1248
1249=item $Result->SourceNameAsHtml
1250
1251(I<Appropriate for B<News> search results>)
1252
1253Similar to C<Title> and C<TitleAsHtml>, but the name of the organization
1254associated with the news article (and, by extension, with C<SourceUrl>).
1255
1256=cut
1257
1258sub SourceName
1259{
1260    return _text_or_html(NewsSource => @_);
1261}
1262
1263sub SourceNameAsHtml
1264{
1265    my $Result = shift; # self
1266    return $Result->SourceName(1);
1267}
1268
1269
1270
1271##############################################################################
1272
1273=item $Result->Language
1274
1275(I<Appropriate for B<News> search results>)
1276
1277A code representing the language in which the article is written (e.g. "en"
1278for English, "ja" for Japanese, etc.). See the list of language codes at
1279C<perldoc> Yahoo::Search.
1280
1281=cut
1282
1283sub Language
1284{
1285    my $Result = shift; # self
1286    return $Result->{Language};
1287}
1288
1289
1290##############################################################################
1291
1292=item $Result->PublishTime
1293
1294=item $Result->PublishWhen
1295
1296(I<Appropriate for B<News> search results>)
1297
1298C<PublishTime> is the Unix time associated with the article, e.g.
1299
1300  print "Published ", scalar(localtime $Result->PublishTime), "\n";
1301
1302C<PublishWhen> gives a string along the lines of
1303
1304  3h 25m ago              (if less than 12 hours ago)
1305  Tue 9:47am              (if less than 5 days ago)
1306  Sat, Dec 25             (if less than 100 days ago)
1307  Sat, Dec 25, 2004       (if >= 100 days ago)
1308
1309B<Bug>: C<PublishWhen> is English-centric.
1310
1311=cut
1312
1313sub PublishTime
1314{
1315    my $Result = shift; # self
1316    if (defined $Result->{PublishDate}) {
1317        return $Result->{PublishDate};
1318    } else {
1319        return ();
1320    }
1321}
1322
1323sub PublishWhen
1324{
1325    my $Result = shift; #self
1326
1327    my $time = $Result->PublishTime;
1328    if (not $time) {
1329        return ();
1330    }
1331
1332    my $delta = time - $time;
1333    if ($delta < 3600 * 12)
1334    {
1335        my $h = int( $delta / 3600);
1336        my $m = int(($delta % 3600)/60 + 0.5);
1337        return "${h}h ${m}m ago";
1338    }
1339
1340    if ($delta < 5 * 3600 * 24)
1341    {
1342        ## give day and time
1343        my ($m,$h, $DOW) = (localtime $time)[1,2,6];
1344        my $ampm = "am";
1345        if ($h == 0) {
1346            $h = 12;
1347        } elsif ($h >= 12) {
1348            $ampm = "pm";
1349            if ($h > 12) {
1350                $h -= 12;
1351            }
1352        }
1353        return sprintf("%s %d:%02d%s", $DOW[$DOW], $h, $m, $ampm);
1354    }
1355
1356    if ($delta < 100 * 3600 * 24)
1357    {
1358        my ($D,$M,$Y,$DOW) = (localtime $time)[3..6];
1359        return sprintf("%s %s %d", $DOW[$DOW], $MON[$M], $D);
1360    }
1361    else
1362    {
1363        my ($D,$M,$Y,$DOW) = (localtime $time)[3..6];
1364        return sprintf("%s %s %d, %04d", $DOW[$DOW], $MON[$M], $D, $Y+1900);
1365    }
1366}
1367
1368
1369##############################################################################
1370
1371=item $Result->Address
1372
1373=item $Result->City
1374
1375=item $Result->State
1376
1377=item $Result->Phone
1378
1379(I<Appropriate for B<Local> search results>)
1380
1381Location and Phone number for the business that the C<Result> refers to.
1382
1383=cut
1384
1385## for local
1386sub Address
1387{
1388    my $Result = shift; # self
1389    return $Result->{Address};
1390}
1391
1392sub City
1393{
1394    my $Result = shift; # self
1395    return $Result->{City};
1396}
1397
1398sub State
1399{
1400    my $Result = shift; # self
1401    return $Result->{State};
1402}
1403
1404sub Phone
1405{
1406    my $Result = shift; # self
1407    return $Result->{Phone};
1408}
1409
1410
1411
1412
1413
1414##############################################################################
1415
1416=item $Result->Miles
1417
1418=item $Result->Kilometers
1419
1420(I<Appropriate for B<Local> search results>)
1421
1422The distance (in miles and kilometers) from the location used to make the
1423query to the location of this result.
1424
1425=cut
1426
1427sub Kilometers
1428{
1429    my $Result = shift; # self
1430    return defined($Result->{Distance}) ? $Result->{Distance} * 1.609 : ();
1431}
1432
1433sub Miles
1434{
1435    my $Result = shift; # self
1436    return defined($Result->{Distance}) ? $Result->{Distance} : ();
1437}
1438
1439
1440
1441
1442
1443##############################################################################
1444
1445=item $Result->Rating
1446
1447(I<Appropriate for B<Local> search results>)
1448
1449Returns the rating associated with the result, if there is one. If there is
1450a rating, it is from 1 (lowest) to 5 (highest) in 0.5-sized steps. If not,
1451nothing is returned.
1452
1453=cut
1454
1455sub Rating
1456{
1457    my $Result = shift; # self
1458    return defined($Result->{Rating}) ? $Result->{Rating} : ();
1459}
1460
1461
1462
1463##############################################################################
1464
1465=item $Result->MapUrl
1466
1467=item $Result->AllMapUrl
1468
1469(I<Appropriate for B<Local> search results>)
1470
1471C<MapUrl> is a url to a Yahoo! Maps map showing the business' location.
1472
1473C<AllMapUrl> is a url to a Yahoo! Maps map showing all the businesses
1474found in the same result-set that the current C<Result> was part of.
1475
1476See the documentation for the C<Url> method for information on the
1477one-argument versions of these methods.
1478
1479=cut
1480
1481sub MapUrl
1482{
1483    my $Result = shift; # self
1484    return _url($Result->{MapUrl}, @_);
1485}
1486
1487sub AllMapUrl
1488{
1489    my $Result = shift; # self
1490    return _url($Result->Response->MapUrl, @_);
1491}
1492
1493
1494
1495##############################################################################
1496
1497=item $Result->BusinessUrl
1498
1499=item $Result->BusinessClickUrl
1500
1501(I<Appropriate for B<Local> search results>)
1502
1503The business' home page, if available. C<BusinessUrl> is appropriate for
1504display, while C<BusinessClickUrl> is appropriate for the href of a link.
1505
1506See the documentation for the C<Url> method for information on the
1507one-argument versions of these methods.
1508
1509=cut
1510
1511sub BusinessUrl
1512{
1513    my $Result = shift; # self
1514    return _url($Result->{BusinessUrl}, @_);
1515}
1516
1517sub BusinessClickUrl
1518{
1519    my $Result = shift; # self
1520    return _url($Result->{BusinessClickUrl} || $Result->{BusinessUrl}, @_);
1521}
1522
1523
1524
1525##############################################################################
1526
1527=item $Result->Term([ I<as_html> ])
1528
1529=item $Result->TermAsHtml
1530
1531(I<Appropriate for B<Spell>, B<Related>, and B<Terms> search results>)
1532
1533C<Term> returns the term associated with the result. If an optional
1534argument is provided and is true, the term text is returned as html.
1535
1536C<TermAsHtml> is the same as
1537
1538  $Result->Term(1)
1539
1540=cut
1541
1542sub Term
1543{
1544    _text_or_html(Term => @_);
1545}
1546
1547sub TermAsHtml
1548{
1549    my $Result = shift; #self
1550    return $Result->Term(1);
1551}
1552
1553
1554##############################################################################
1555
1556=pod
1557
1558=back
1559
1560=head1 Author
1561
1562Jeffrey Friedl (jfriedl@yahoo.com)
1563
1564=cut
1565
15661;