RSS Git Download  Clone
Raw Blame History
package LIMS::Controller::Roles::Aspell;

use Moose::Role;

use Text::Aspell;

has speller => ( is => 'ro', isa => 'Text::Aspell', lazy_build => 1 );
has id_generator => ( is => 'rw', isa => 'Str' );

has seen => (
    is        => 'ro',
    isa       => 'HashRef[Str]',
    default   => sub { {} },
	lazy      => 1,
    traits    => ['Hash'],
    handles   => {
        has_seen => 'get',
		set_seen => 'set',
    },
);

has words => (
    is        => 'ro',
    isa       => 'HashRef[Str]',
    default   => sub { {} },
	lazy      => 1,
	traits  => ['Hash'],
	handles => {
		has_word => 'get',
		set_word => 'set',
	},
);

has word_data => (
    is        => 'ro',
    isa       => 'ArrayRef[HashRef]',
    default   => sub { [] },
	lazy      => 1,
	traits  => ['Array'],
	handles => {
		add_word_data   => 'push',
		all_word_data   => 'elements',
        clear_word_data => 'clear',
	},
);

#-------------------------------------------------------------------------------
# idea borrowed from http://sedition.com/perl/cgi-javascript-spell.html
sub do_spellcheck { 
	my ($self, $fieldname) = @_;

    my $speller = $self->speller; # $self->debug($speller->list_dictionaries);

	my $text = $self->query->param($fieldname); # warn $text;
	
	WORD:
    while ( $text =~ /(['\w]+)/g ) { # add apos back
        my $word = $1;
		
		# skip numbers, CD4, etc:
		next WORD if $self->_is_unsupported($word);
		# skip if already seen:
        next WORD if $self->has_seen($word);		
		
		# skip if word recognised in dictionary(s):
        unless ( $speller->check($word) ) { # warn $word;
			# add unrecognised word to 'words' collection:
			$self->set_word($word => 1);

			# get unique id for div:
			my $id = $self->id_generator; # warn $id;
			my $div_id = join '_', $fieldname, $id; # warn $div_id;
			
            my @suggestions = map {
				{
					escaped => quotemeta($_), # so apostrophied words work OK
					display => $_, # for <a href> display
				}
			} $speller->suggest($word);
			
            # eg, to restrict to 5: 
            # @suggestions = splice(@sug,0,6) if @suggestions > 6;
			
			my %data = (
				id 			=> $div_id,
				word 		=> quotemeta($word),
				fieldname   => $fieldname,
				suggestions => \@suggestions,
			);
			$self->add_word_data(\%data);
			
			# increment id_generator for next time:
			$self->id_generator(++$id);
        }
		# add word to 'seen' collection:
		$self->set_seen($word => 1); 
    }

	my @data = $self->all_word_data;
	# reset for any more textareas:
	$self->clear_word_data;
	
	return \@data;
}

#-------------------------------------------------------------------------------
sub colourise_text {	
	my ($self, $fieldname) = @_;

	my $text = $self->query->param($fieldname); # warn $text;
	
	my $q = $self->query;
	
	my @words;	
    while ( $text =~ /(['\w]+)/g ) { # add apos back
        my $word = $1;
	
		push @words, $self->has_word($word)
			? $q->span({-class=>'unrecognised'}, $word) # highlight it
			: $word;
	}
	
	return \@words;
}

#-------------------------------------------------------------------------------
sub _build_speller {
	my $self = shift;
	
    my $speller = Text::Aspell->new;

	# must be read/write by the webserver user - for location of personal dict:
	# $self->cfg->{path_to_www_docs} already has end slash:
	my $home_dir = $self->cfg->{path_to_www_docs} . 'speller/dicts';
	my $medical  = $home_dir . '/en-medical.rws';
	
    $speller->set_option('lang','en_EN');
    $speller->set_option('sug-mode','fast');
    $speller->set_option('home-dir', $home_dir);
    # extra-dicts needs manually rebuilding - suggest using words from personal dict:
	# $speller->set_option('extra-dicts', $medical);

    return $speller;
}

#-------------------------------------------------------------------------------
sub _is_unsupported {
	my $self = shift;
	my $word = shift;
	
	return 1 if $word =~ /\A(CD?)\d+\Z/; # CD4 or just numbers	
}

1;