CatalystX::Usul::Plugin::Model::StashHelper - Convenience methods for stuffing the stash


CatalystX-Usul documentation Contained in the CatalystX-Usul distribution.

Index


Code Index:

Name

Top

CatalystX::Usul::Plugin::Model::StashHelper - Convenience methods for stuffing the stash

Version

Top

0.3.$Revision: 576 $

Synopsis

Top

   package CatalystX::Usul;
   use parent qw(Catalyst::Component CatalystX::Usul::Base);

   package CatalystX::Usul::Model;
   use parent qw(CatalystX::Usul CatalystX::Usul::StashHelper);

   package YourApp::Model::YourModel;
   use parent qw(CatalystX::Usul::Model);

Description

Top

Many convenience methods for stuffing/resetting the stash. The form widget definitions will be replaced later by the form building method which is called from the HTML view

Subroutines/Methods

Top

add_append

Stuff some content into the stash so that it will appear in the append div in the template. The content is a hash ref which will be interpreted as a widget definition by the form builder which is invoked by the HTML view. Multiple calls push the content onto a stack which is rendered in the order in which it was stacked

add_button

Add a button definition to the stash. The template will render these as image buttons on the bbar div

add_buttons

Loop around add_button

add_chooser

Generates the data for the popup chooser window which allows a data value to be selected from a list produced by some query. It is intended as a replacement for a popup menu widget where the list of values would be prohibitively long

add_error

Stringifies the passed error object, localises the text, logs it as an error and calls add_result to display it at the top of the sdata div

add_error_msg

Localises the message text, creates a new error object and calls add_error

add_field

Create a widget definition for a form field

add_header

Stuffs the stash with the data for the page header

add_hidden

Adds a hidden field to the form

add_result

Adds the result of forwarding to an an action. This is the result div in the template

add_result_msg

Localises the message text and calls add_result

add_sidebar_panel

Stuffs the stash with the data necessary to create a panel in the accordion widget on the sidebar

check_field_wrapper

   $model->check_field_wrapper;

Extract parameters from the query and call check_field. Stash the result

clear_append

Clears the stash of the widget data used by the region appended to the main data store

clear_buttons

Clears button data from the stash

clear_controls

Groups the methods that clear the stash of data not used in a minority of pages

clear_form

Initialises the sdata div contents. Called by /stash_content on first use

clear_hidden

Clears the hidden fields from the form

clear_menus

Clears the stash of the main navigation and tools menu data

clear_result

Clears the stash of messages from the output of actions

clear_sidebar

Clears the stash of the data used by the sidebar accordion widget

group_fields

Stashes the data used by HTML::FormWidgets to throw fieldset around a group of fields

open_window

Returns the Javascript fragment that will open a new window in the web browser

search_page

Create a KinoSearch results page

simple_page

Creates a "simple" page from information stored in the configuration files

stash_content

Pushes the content (usually a widget definition) onto the specified stack

stash_form

Calls stash_content specifying the sdata stack

stash_meta

Adds some meta data to the response for an Ajax call

Configuration and Environment

Top

None

Diagnostics

Top

None

Dependencies

Top

CatalystX::Usul
Data::Pageset
Lingua::Flags
Time::Elapsed

Incompatibilities

Top

There are no known incompatibilities in this module.

Bugs and Limitations

Top

There are no known bugs in this module. Please report problems to the address below. Patches are welcome

Author

Top

Peter Flanigan, <Support at RoxSoft.co.uk>

License and Copyright

Top


CatalystX-Usul documentation Contained in the CatalystX-Usul distribution.

# @(#)$Id: StashHelper.pm 576 2009-06-09 23:23:46Z pjf $

package CatalystX::Usul::Plugin::Model::StashHelper;

use strict;
use warnings;
use version; our $VERSION = qv( sprintf '0.3.%d', q$Rev: 576 $ =~ /\d+/gmx );
use parent qw(CatalystX::Usul);

use Data::Pageset;
use Lingua::Flags;
use Time::Elapsed qw(elapsed);

my $DOTS = chr 8230;
my $NUL  = q();
my $SEP  = q(/);
my $SPC  = q( );
my $TTS  = q( ~ );

# Core stash helper methods

sub stash_content {
   # Push the content onto the item list for the given stash id
   my ($self, $content, $id, $clear) = @_;

   return unless ($content and $id);

   my $s = $self->context->stash;

   eval { $self->$clear() } if ($clear and not defined $s->{ $id });

   my $item = { content => $content, id => $id };

   if (ref $content eq q(HASH) and $content->{class}) {
      $item->{class} = $content->{class};
   }

   push @{ $s->{ $id }->{items} }, $item;
   $s->{ $id }->{count} = @{ $s->{ $id }->{items} };
   return;
}

sub stash_meta {
   # Set attribute value pairs for the given stash id
   my ($self, $data, $id, $clear) = @_;

   $id ||= q(sdata); $clear ||= q(clear_form);

   my $s = $self->context->stash;

   eval { $self->$clear() } unless (defined $s->{ $id });

   while (my ($attr, $value) = each %{ $data }) {
      $s->{ $id }->{ $attr } = $value unless ($attr eq q(items));
   }

   return;
}

# Stash content methods

sub add_append {
   # Add a widget definition to the append div
   my ($self, $content) = @_;

   return unless ($content and ref $content eq q(HASH));

   $content->{widget} = 1;

   $self->stash_content( $content, q(append), q(clear_append) );
   return;
}

sub add_button {
   # Add a button widget definition to the button bar div
   my ($self, $args) = @_; my $s = $self->context->stash; my $content;

   return unless ($args and ref $args eq q(HASH));

   my $label   = $args->{label  } || q(Unknown);
   my $button  = $s->{buttons}->{ $s->{form}->{name}.q(.).(lc $label) };
   my $help    = $args->{help   } || $button ? $button->{help  } : $NUL;
   my $prompt  = $args->{prompt } || $button ? $button->{prompt} : $NUL;
   my $onclick = $args->{onclick} || $prompt
               ? "return window.confirm( '${prompt}' )" : $NUL;
   my $type    = $args->{type   } || q(image);

   $label      = (split $SPC, $label.q( X))[0];

   my $file    = $label.q(.png);
   my $path    = $self->catfile( $s->{skindir}, $s->{skin}, $file );

   unless (-f $path) {
      $file = $label.q(.gif);
      $path = $self->catfile( $s->{skindir}, $s->{skin}, $file );
   }

   if ($type eq q(image) and -f $path) {
      $content = { alt => $label, src => $s->{assets}.$file };
   }

   $content->{class  } = $args->{class} || q(button);
   $content->{name   } = $label;
   $content->{onclick} = $onclick if ($onclick);
   $content->{tip    } = ($args->{title} || $DOTS).$TTS.$help if ($help);
   $content->{type   } = q(button);
   $content->{widget } = 1;

   $self->stash_content( $content, q(bbar), q(clear_buttons) );
   return;
}

sub add_footer {
   my $self = shift;
   my $c    = $self->context;
   my $cfg  = $c->config;
   my $req  = $c->req;
   my $s    = $c->stash;
   my ($content, $text);

   $self->stash_content( $self->_footer_line, q(footer), q(clear_footer) );

   $content = { text    => $s->{user}.q(@).$s->{host_port},
                tip     => $self->loc( q(yourIdentity) ),
                tiptype => q(plain),
                type    => q(label),
                widget  => 1 };
   $self->stash_content( $content, q(footer) );

   if ($s->{debug}) {
      # Useful numbers and such
      if ($cfg->{version}) {
         $content = { text    => q(&nbsp;).$cfg->{version},
                      tip     => $self->loc( q(moduleVersion) ),
                      tiptype => q(plain),
                      type    => q(label),
                      widget  => 1 };
         $self->stash_content( $content, q(footer) );
      }

      if (defined $s->{version}) {
         $content = { text    => q(&nbsp;).$s->{version},
                      tip     => $self->loc( q(levelVersion) ),
                      tiptype => q(plain),
                      type    => q(label),
                      widget  => 1 };
         $self->stash_content( $content, q(footer) );
      }

      ($text = $self->stamp) =~ tr?/:?..?;
      $content = { text    => $text,
                   tip     => $self->loc( q(pageGenerated) ),
                   tiptype => q(plain),
                   type    => q(label),
                   widget  => 1 };
      $self->stash_content( $content, q(footer) );

      if ($s->{elapsed}) {
         $content = { text    => q(&nbsp;).elapsed( $s->{elapsed} ),
                      tip     => $self->loc( q(elapsedTime) ),
                      tiptype => q(plain),
                      type    => q(label),
                      widget  => 1 };
         $self->stash_content( $content, q(footer) );
      }

      # TODO: Replace this with a language selector
      my %lang2country_map = ( 'de' => q(DE), 'en' => q(GB) );
      my $country          = $lang2country_map{ $s->{lang} };
      my $flag             = as_html_img( $country );

      $flag =~ s{ \s* / > }{>}mx if ($s->{content_type} eq q(text/html));

      $content = { text => $flag, type => q(label), widget => 1 };
      $self->stash_content( $content, q(footer) );
   }

   return;
}

sub add_header {
   my $self = shift; my $s = $self->context->stash;

   $self->stash_content( $self->_logo_link,        q(header) );
   $self->stash_content( $self->_company_link,     q(header) );
   $self->stash_meta   ( { title => $s->{title} }, q(header) );
   return;
}

sub add_hidden {
   # Add a hidden input field to the form
   my ($self, $name, $values) = @_;

   return unless ($name && defined $values);

   $values = [ $values ] unless (ref $values eq q(ARRAY));

   for my $value (@{ $values }) {
      my $content = { default => $value,    name   => $name,
                      type    => q(hidden), widget => 1 };

      $self->stash_content( $content, q(hidden), q(clear_hidden) );
   }

   return;
}

sub add_result {
   # Add some content the the result div
   my ($self, $content) = @_;

   return unless ($content); chomp $content;

   my $s = $self->context->stash;

   if ($s->{result} and $s->{result}->{items}->[ 0 ]) {
      $s->{result}->{items}->[-1]->{content} .= "\n";
   }

   $self->stash_content( $content, q(result), q(clear_result) );
   return;
}

sub add_sidebar_panel {
   # Add an Ajax call to the side bar accordion widget
   my ($self, $args) = @_; my ($content, $count, $jscript);

   my $s = $self->context->stash;

   unless ($count = $s->{sidebar}->{count} || 0) {
      $self->_clear_by_id( q(sidebar) );
      $s->{sidebar}->{tip} = $self->loc( q(sidebarTip) );
   }

   if ($args->{name} eq q(default)) {
      $content = {
         content      => { class => q(sidebarContent),
                           id    => q(default),
                           text  => $self->loc( q(sidebarBlankContent) ) },
         contentClass => q(sidebarPanel),
         contentId    => q(panel).$count.q(Content),
         header       => { content => $self->loc( q(sidebarBlankHeader)) },
         headerClass  => q(sidebarHeader sidebarHeaderFirst),
         headerId     => q(glassHeader),
         panelId      => q(glassPanel) };
   }
   else {
      $jscript  = "behaviour.loadMore.request('".$args->{name}."', '";
      $jscript .= $args->{name}."', '".($args->{value} || $NUL)."')";

      $args->{heading} = ucfirst $args->{name} unless ($args->{heading});

      $content = {
         content      => { class => q(sidebarContent),
                           id    => $args->{name},
                           text  => q(&nbsp;) },
         contentClass => q(sidebarPanel),
         contentId    => q(panel).$count.q(Content),
         header       => { onclick => $jscript,
                           content => $args->{heading} },
         headerClass  => q(sidebarHeader),
         headerId     => $args->{name}.q(Header),
         panelId      => $args->{name}.q(Panel) };
   }

   $self->stash_content( $content, q(sidebar), q(clear_sidebar) );
   return $s->{sidebar}->{count} - 1;
}

sub stash_form {
   my ($self, $content) = @_;

   $self->stash_content( $content, q(sdata), q(clear_form) );
   return;
}

# Clear content methods. Called by the stash content methods on first use

sub clear_append {
   my ($self, $args) = @_; $self->_clear_by_id( q(append), $args ); return;
}

sub clear_buttons {
   my ($self, $args) = @_; $self->_clear_by_id( q(bbar), $args ); return;
}

sub clear_footer {
   my ($self, $args) = @_; $self->_clear_by_id( q(footer), $args ); return;
}

sub clear_form {
   # Clear the stash of all form content
   my ($self, $args) = @_; my $s = $self->context->stash; my $id = q(sdata);

   return if (exists $s->{ $id });

   $self->_clear_by_id( $id, $args );

   if (exists $args->{title}) {
      $s->{title} = $args->{title}; $s->{header}->{title} = $args->{title};
   }

   $s->{firstfld} = $args->{firstfld} || $NUL;
   return;
}

sub clear_hidden {
   my ($self, $args) = @_; $self->_clear_by_id( q(hidden), $args ); return;
}

sub clear_menus {
   my $self = shift; $self->_clear_by_id( q(menus) ); return;
}

sub clear_quick_links {
   my $self = shift; $self->_clear_by_id( q(quick_links) ); return;
}

sub clear_result {
   my ($self, $args) = @_; my $s = $self->context->stash;

   $self->_clear_by_id( q(result), $args );
   $s->{result}->{class} = q(centre);
   $s->{result}->{text } = 'Results';
   return;
}

sub clear_sidebar {
   my $self = shift; $self->context->stash( sidebar => 0 ); return;
}

# Curried stash content methods

sub add_buttons {
   my ($self, @buttons) = @_; my $title = $self->loc( q(buttonTitle) );

   for (0 .. $#buttons) {
      $self->add_button( { label => $buttons[ $_ ], title => $title } );
   }

   return;
}

sub add_chooser {
   my ($self, $args) = @_; my ($jscript, $param);

   my $attr   = $args->{attr};
   my $field  = $args->{field};
   my $form   = $args->{form};
   my $method = $args->{method};
   my $val    = $args->{value};
   my $w_fld  = $args->{where_fld};
   my $w_val  = $args->{where_val};

   $param->{ $w_fld } = $w_val if ($w_fld);
   $param->{ $field } = { like => $val ? $val : q(%) };

   my @items  = $self->$method( $param );

   unless ($items[0]) {
      $self->add_field( { text => $self->loc( 'Nothing selected' ),
                          type => q(label) } );
      return;
   }

   for my $item (@items) {
      $jscript  = "behaviour.submit.returnValue('";
      $jscript .= "${form}', '${field}', '".$item->$attr()."') ";
      $self->add_field( { class   => $args->{class},
                          clear   => q(left),
                          href    => '#top',
                          onclick => $jscript,
                          text    => $item->$attr(),
                          tip     => $self->loc( 'Click to select' ),
                          type    => q(anchor) } );
   }

   my $s = $self->context->stash;

   $s->{is_popup} = q(true); # Stop JS from caching window size
   $s->{header  }->{title} = $self->loc( 'Select Item' );
   delete $s->{token};
   return;
}

sub add_error {
   # Handle $self->catch error thrown by a call to the model
   my ($self, $e, $verbosity, $offset) = @_;

   unless (defined $verbosity) {
      $verbosity = $self->context->stash->{debug} ? 3 : 2;
   }

   $offset = 1 unless (defined $offset);

   my $err = $self->loc( $e->as_string( $verbosity, $offset ), @{ $e->args } );

   $self->log_error ( (ref $self).$SPC.(split m{ \n }mx, $err)[0] );
   $self->add_result( $err );
   return;
}

sub add_error_msg {
   my ($self, @rest) = @_;

   $self->add_error( $self->exception_class->new( $self->loc( @rest ) ) );
   return;
}

sub add_field {
   # Add a field widget definition to the inner frame div
   my ($self, $content) = @_; my $s = $self->context->stash;

   return unless ($content and ref $content eq q(HASH));

   if (exists $content->{subtype} && $content->{subtype} eq q(html)) {
      $s->{content}->{style} = q(overflow: hidden; padding: 0px;);
   }

   $content->{widget} = 1;
   $self->stash_form( $content );
   return;
}

sub add_result_msg {
   my ($self, @rest) = @_; $self->add_result( $self->loc( @rest ) ); return;
}

sub add_search_links {
   my ($self, $page_info, $attrs) = @_; my ($key, $name, $page, $ref);

   $attrs ||= {};

   my $s            = $self->context->stash;
   my $expr         = $attrs->{expression};
   my $hits_per     = $attrs->{hits_per};
   my $href         = $s->{form}->{action}.$SEP.$expr.$SEP;
   my $anchor_class = $attrs->{anchor_class} || q(searchFade smaller);
   my $clear        = 1;

   for $page (qw(first_page previous_page pages_in_set next_page last_page)) {
      if ($page eq q(pages_in_set)) {
         for (@{ $page_info->pages_in_set }) {
            if ($_ == $page_info->current_page) {
               $ref = { container => 1,
                        text      => q(&hellip;), type => q(label) };
            }
            else {
               $ref = { class  => $anchor_class,
                        href   => $href.$hits_per.$SEP.$_,
                        name   => q(page).$_,
                        pwidth => 0,
                        text   => $_,
                        type   => q(anchor) };
            }

            $self->add_field( $ref );
         }
      }
      elsif ($key = $page_info->$page) {
         $name = (split m{ _ }mx, $page)[0];
         $ref  = { class  => $anchor_class,
                   href   => $href.$hits_per.$SEP.$key,
                   name   => $name,
                   pwidth => 0,
                   text   => $self->loc( $page.q(_anchor) ),
                   type   => q(anchor) };

         if ($clear) {
            $ref->{class } .= q( clearLeft);
            $ref->{prompt}  = $self->loc( q(page_prompt) );
         }

         $self->add_field( $ref );
         $clear = 0;
      }
   }

   return;
}

sub group_fields {
   # Enclose a group of form fields in a field set definition
   my ($self, $args) = @_; my $text;

   my $nitems = $args->{nitems} || $args->{nItems};
   my $class  = exists $args->{class} ? $args->{class} : q(fullWidth);

   return if (!$nitems || $nitems <= 0);

   $text = $args->{text} || $self->loc( $args->{id} || q(duh) );

   $self->stash_form( { class  => $class,  group => 1,
                        nitems => $nitems, text  => $text } );
   return;
}

sub search_page {
   my ($self, $args) = @_; my ($e, $hit, @hits, $link_num, $page_info, $text);

   my $cnt      = 0;
   my $expr     = $args->{expression};
   my $excerpts = $args->{excerpts};
   my $hits_per = $args->{hits_per};
   my $key      = $args->{key};
   my $model    = $args->{data_model};
   my $offset   = $args->{offset};
   my $s        = $self->context->stash;
   my $form     = $s->{form}->{name};
   my $ref      = eval { $model->search_for( $expr, $hits_per, $offset ) };

   return $self->add_error( $e ) if ($e = $self->catch);

   while ($hit = $ref->fetch_hit_hashref) { push @hits, $hit; $cnt++ }

   $ref        = { current_page     => $offset + 1,
                   entries_per_page => $hits_per,
                   mode             => q(slide),
                   total_entries    => $ref->total_hits };
   $page_info  = Data::Pageset->new( $ref );
   $link_num   = 1 + $hits_per * $offset;
   $text       = $self->loc( $key, $expr );
   $self->add_field( { id => $form.q(.).$key, text => $text } );
   $text       = $self->loc( q(search_results), $offset +1,
                             $page_info->last_page,
                             $cnt, $ref->{total_entries} );
   $self->add_field( { id => $form.q(.search_results), text => $text } );
   $self->add_search_links( $page_info, { expression => $expr,
                                          hits_per   => $hits_per } );

   for $hit (@hits) {
      $self->add_field( { href   => $s->{url}.$hit->{url},
                          id     => $form.q(.title),
                          stepno => $link_num++,
                          text   => $hit->{title} } );
      $self->add_field( { id     => $form.q(.excerpt),
                          text   => $hit->{excerpts}->{ $excerpts } } );
      $self->add_field( { id     => $form.q(.score),
                          pwidth => 0,
                          text   => sprintf '%0.3f', $hit->{score} } );
      $self->add_field( { id     => $form.q(.file),
                          pwidth => 0,
                          text   => $hit->{file} } );
      $self->add_field( { id     => $form.q(.key),
                          pwidth => 0,
                          text   => $hit->{key} } );
   }

   $self->add_search_links( $page_info, { expression => $expr,
                                          hits_per   => $hits_per } );
   return;
}

sub simple_page {
   # Knock up a page of simple content from the XML config files
   my ($self, $name, $n_cols) = @_; my ($page, $ref, $subh, $text);

   my $s = $self->context->stash;

   return $self->add_error_msg( 'No page specified' ) unless ($name);

   unless ($page = $s->{pages}->{ $name }) {
      return $self->add_error_msg( 'Page [_1] unknown', $name );
   }

   unless (exists $s->{sdata}) {
      delete $s->{token}; # Do not need a CSRF token on a simple page
      $self->clear_form
         ( { heading    => $page->{heading} || $s->{title},
             subHeading => { content => $page->{subHeading} || q(&nbsp;) },
             title      => $page->{title} || $s->{title} } );
   }

   my $idx  = 0;
   my $data = { values => [] };
   my $para = $page->{vals}->{ q(para).$idx };

   while ($text = $para->{text}) {
      my $drop = $para->{dropcap} || 0; my $mark = $para->{markdown} || 0;

      $ref  = { class => q(), text => { dropcap => $drop, markdown => $mark,
                                        text    => $text, type => q(label) } };

      if ($subh = $page->{vals}->{ q(subHeading).$idx }->{text}) {
         $ref->{heading} = { text => $subh, type => q(label) };
      }

      push @{ $data->{values} }, $ref;
      $para = $page->{vals}->{ q(para).++$idx };
   }

   my $columns   = $n_cols || $page->{columns};
   my $col_class = ($columns > 1 ? 'multi' : 'one').'Column '.$page->{class};

   $self->add_field( { class           => q(fullWidth),
                       column_class    => $col_class,
                       columns         => $columns,
                       container       => 1,
                       container_class => q(paragraphs centre),
                       data            => $data,
                       hclass          => q(subheading),
                       type            => q(paragraphs) } );
   return 1;
}

# Stash meta methods

sub check_field_wrapper {
   # Process Ajax calls to validate form field values
   my $self = shift;
   my $s    = $self->context->stash;
   my $id   = $self->query_value( q(id) );
   my $val  = $self->query_value( q(val) );
   my $e;

   delete $s->{token};
   $self->stash_meta( { id => $id.q(_checkField), result => q(hidden) } );

   eval { $self->check_field( $id, $val ) };

   return unless ($e = $self->catch);

   if ($s->{debug}) {
      $self->log_debug( $self->loc( $e->as_string( 1 ), $id, $val ) );
   }

   $self->stash_meta( { result => q(error) } );
   return;
}

# Supporting cast

sub clear_controls {
   # Clear contents of multiple divs
   my $self = shift;

   $self->clear_footer;
   $self->clear_menus;
   $self->clear_quick_links;
   $self->clear_sidebar;
   return;
}

sub open_window {
   my ($self, @rest) = @_; my ($jscript, $text);

   my $args = $self->arg_list( @rest );

   return unless ($args->{key} and $args->{href});

   $text     = 'dependent=no, width='.($args->{width} || 800);
   $text    .= ', height='.($args->{height} || 600).', resizable=yes, ';
   $text    .= 'screenX=0, screenY=0, titlebar=no, scrollbars=yes';
   $jscript  = "behaviour.window.openWindow('".$args->{href}."', '";
   $jscript .= $args->{key}."', '${text}')";
   return $jscript;
}

# Private methods

sub _clear_by_id {
   my ($self, $id, $args) = @_;

   return unless ($id);

   my $s = $self->context->stash; $s->{ $id } ||= {}; $args ||= {};

   $s->{ $id }->{count     } = 0;
   $s->{ $id }->{heading   } = $args->{heading} || $NUL;
   $s->{ $id }->{items     } = [];
   $s->{ $id }->{subHeading} = $args->{subHeading}
      if (exists $args->{subHeading});

   return;
}

sub _company_link {
   my $self    = shift;
   my $s       = $self->context->stash;
   my $href    = $self->uri_for( q(root).$SEP.q(company), $s->{lang} );
   my $tip     = $DOTS.$TTS.$self->loc( q(aboutCompanyTip) );
   my $content =
      { class           => q(headerFade),
        container_class => $s->{class},
        container_id    => q(headerSubTitle),
        href            => '#top',
        onclick         => $self->open_window( key  => q(company),
                                               href => $href ),
        sep             => q(),
        text            => $s->{company},
        tip             => $tip,
        type            => q(anchor),
        widget          => 1 };

   return $content;
}

sub _footer_line {
   my $self = shift; my ($content, $item, $jscript, $tip);

   my $s = $self->context->stash;

   # Cut on the dotted line toggle the footer visibilty
   $item     = 1 + ($s->{is_administrator} ? 1 : 0);
   $jscript  = "behaviour.state.toggleSwapText('tools0item${item}";
   $jscript .= "', 'footer', '".$self->loc( q(footerOffText) )."', '";
   $jscript .= $self->loc( q(footerOnText) )."')";
   $tip      = $self->loc( q(footerToggleTip) );
   $content  = { alt      => 'Close Footer',
                 class    => q(footer),
                 href     => '#top',
                 imgclass => q(footer),
                 onclick  => $jscript,
                 text     => $s->{assets}.'footerCut.gif',
                 tip      => $tip,
                 type     => q(rule),
                 widget   => 1 };

   return $content;
}

sub _logo_link {
   my $self    = shift;
   my $s       = $self->context->stash;
   my $href    = $s->{server_home} || 'http://'.$s->{domain};
   my $content =
      { class           => q(logo),
        container_class => $s->{class},
        container_id    => q(companyLogo),
        fhelp           => q(Company Logo),
        hint_title      => $href,
        href            => $href,
        imgclass        => q(logo),
        sep             => $NUL,
        text            => $s->{assets}.($s->{logo} || q(logo.png)),
        tip             => $self->loc( q(logoTip) ),
        type            => q(anchor),
        widget          => 1 };

   return $content;
}

1;

__END__

# Local Variables:
# mode: perl
# tab-width: 3
# End: