added sol100 and chado cvterm pages to validate_all.t
[sgn.git] / lib / CXGN / Page / Secretary.pm
blob667f3ac867b000157dd652e24ea528841c666a8b
1 package CXGN::Page::Secretary;
2 use strict;
4 use CXGN::Login;
5 use CXGN::People;
6 use CXGN::Contact;
7 use CXGN::Cookie;
8 use CXGN::UserPrefs;
9 use CXGN::DB::Connection;
10 use CXGN::Page::Widgets;
11 use HTML::Entities;
13 use base('CXGN::Page');
15 sub new {
16 my $class=shift;
17 my $page_name = shift;
18 my $author = shift;
20 ## I've decided to make the Secretary Page module create the default database handle for the Page-calling script
22 my $self = $class->SUPER::new($page_name, $author);
24 my $dbh = CXGN::DB::Connection->new();
25 $dbh->do("SET SEARCH_PATH=tsearch2,public,sgn_people");
26 $self->{dbh} = $dbh;
28 my %evidefs;
29 my $evidef_q = $dbh->prepare("SELECT * FROM ara_evidef");
30 $evidef_q->execute();
31 while(my $row = $evidef_q->fetchrow_hashref) {
32 $evidefs{$row->{code}} = $row->{description};
34 $self->{evicode2definition} = \%evidefs;
35 $self->{hotlist_button_ids} = {};
37 #The project name will still be SGN for cookie compatibility, but we use the Secretary Page/VHost file
38 $self->{page_module}="CXGN::Page::VHost::Secretary";
39 eval "require $self->{page_module}";
40 if($@){
41 die("Secretary VHost Page module ($self->{page_module}) not found");
43 $self->{page_object}=$self->{page_module}->new($self->{dbh});
44 $self->fetch_arguments();
46 return $self;
49 sub fetch_arguments {
50 my $self = shift;
51 ( $self->{gene}, $self->{searchQuery}, $self->{noCheck}, $self->{leftBound}, $self->{querySize},
52 $self->{referenceGene}, $self->{prevQuery}, $self->{prevLB}, $self->{physicalMode}, $self->{username},
53 $self->{pass}, $self->{logout}, $self->{newUser}, $self->{passRep}, $self->{email},
54 $self->{fname}, $self->{lname}, $self->{org}, $self->{error404}
56 = map { HTML::Entities::decode($_) }
57 $self->get_encoded_arguments
59 'g', 'query', 'nocheck', 'lb', 'qsize',
60 'referenceGene', 'prevQ', 'prevLB', 'physicalMode', 'username',
61 'password', 'logout', 'newuser', 'passwordrep', 'email',
62 'fname', 'lname', 'org', 'error404'
65 if(!($self->{leftBound} =~ /^\d+$/) || $self->{leftBound} <= 0) {
66 $self->{leftBound} = 1;
69 $self->{gene} ||= "";
70 $self->{searchQuery} ||= "";
71 #$self->{searchQuery} =~ s/^\s*(.*?)\s*$/$1/;
73 my $querySize = $self->{querySize};
74 if(defined $querySize && (!($querySize =~ /^\d+$/) || $querySize<=0)) { undef $querySize }
75 $self->{querySize} = $querySize;
77 my $physicalMode = 0;
78 if($self->{referenceGene}) { $physicalMode = 1}
79 $self->{physicalMode} ||= $physicalMode;
82 ### User account functions #########################################
84 sub login {
85 my $self = shift;
86 my ($username, $password) = ($self->{username}, $self->{pass});
87 $username ||= '';
88 $password ||= '';
89 my $login_controller = CXGN::Login->new({NO_REDIRECT=>1});
90 my $login_info=$login_controller->login_user($username,$password);
91 $self->{login_info} = $login_info;
92 return $login_info;
95 sub logout {
96 my $logout_controller = CXGN::Login->new({NO_REDIRECT=>1});
97 $logout_controller->logout_user();
100 sub new_user {
101 my $self = shift;
103 my ($first_name, $last_name, $username, $password, $confirm_password, $email_address)
104 = ($self->{fname}, $self->{lname}, $self->{username}, $self->{pass}, $self->{passRep}, $self->{email});
106 if ($username) {
108 # check password properties...
110 my @fail = ();
111 if (length($username) < 7) {
112 push @fail, "Username is too short. Username must be 7 or more characters";
113 } else {
114 # does user already exist?
116 my $existing_login = CXGN::People::Login -> get_login($username);
118 if ($existing_login->get_username()) {
119 push @fail, "Username \"$username\" is already in use. Please pick a different username.";
123 if (length($password) < 7) {
124 push @fail, "Password is too short. Password must be 7 or more characters";
126 if ("$password" ne "$confirm_password") {
127 push @fail, "Password and confirm password do not match.";
129 if ($password eq $username) {
130 push @fail, "Password must not be the same as your username.";
132 if ($email_address !~ m/[^\@]+\@[^\@]+/) {
133 push @fail, "Email address is invalid.";
137 if (@fail) {
138 return new_user_fail(\@fail);
141 my $confirm_code = $self->tempname();
142 my $new_user = CXGN::People::Login->new();
143 $new_user -> set_username($username);
144 $new_user -> set_password($password);
145 $new_user -> set_pending_email($email_address);
146 $new_user -> set_confirm_code($confirm_code);
147 # $new_user -> set_disabled('unconfirmed account');
148 $new_user -> store();
150 #this is being added because the person object still uses two different objects, despite the fact that we've merged the tables
151 my $person_id=$new_user->get_sp_person_id();
152 my $new_person=CXGN::People::Person->new($person_id);
153 $new_person->set_first_name($first_name) if ($first_name);
154 $new_person->set_last_name($last_name) if ($last_name);
155 $new_person->store();
157 $self->{person_id} = $person_id;
158 return "success";
160 else {
161 return "Username not provided";
165 sub new_user_fail {
166 my ($fail_ref) = @_;
168 my $fail_str = "";
169 foreach ( @{$fail_ref} ) {
170 $fail_str .= "<li>$_</li>\n"
173 return <<END_HEREDOC;
175 <table summary="" width=80% align=center>
176 <tr><td style='color:black'>
177 <p style='color:#dd4444'>Your account could not be created for the following reasons</p>
179 <ul>
180 $fail_str
181 </ul>
182 </td></tr>
183 <tr><td><br /></td></tr>
184 </table>
185 END_HEREDOC
189 sub set_hotlist {
190 my $self = shift;
191 unless($self->{hotlist}){
192 $self->{hotlist} = $self->{page_object}->get_hotlist();
195 if($self->{hotlist}){
196 $self->{hotlist_content} = $self->{hotlist}->get_item_contents();
198 return $self->{hotlist};
201 sub get_login_info {
202 my $self = shift;
204 my $just_logged_in = 0; #we can't check a cookie that we just set, so use this to properly display page elements on first page-load of login
205 my $login_warning = '';
206 my $extra_notification = '';
207 my $login_info_person_id = 0;
208 my $login_info;
211 if($self->{newUser}){
212 $login_warning = $self->new_user();
213 $login_info = $self->login();
214 $extra_notification = "New account with the username \"$self->{username}\" created!";
216 elsif($self->{username}){
217 $login_info = $self->login();
218 if($login_info->{incorrect_password}) { $login_warning = "You provided an incorrect password for this account." }
219 if($login_info->{account_disabled}) {
220 $login_warning = "Your account has been disabled. Please contact <a href='mailto:support\@sgn.cornell.edu'>support\@sgn.cornell.edu</a>";
222 if($login_info->{duplicate_cookie_string}) { $login_warning = "A duplicate cookie string has been issued (rare). Please try again." }
223 if($login_info->{logins_disabled}) { $login_warning = "Sorry, logins have been temporarily disabled on this system" }
224 if(!$login_warning) {
225 $login_warning = "success";
226 $login_info_person_id = $login_info->{person_id};
230 # overrides username login_info handler, which has to remain in place to make warnings (i should change this)
231 my $loginh = CXGN::Login->new({NO_REDIRECT=>1});
232 my $extra_warning;
233 if(my $sp_person_id = $loginh->has_session()){
234 $login_info = $loginh->get_login_info();
235 $self->{validUser} = 1;
237 if($login_warning eq "success") {
238 $login_warning = '';
239 $just_logged_in = 1;
240 $self->{validUser} = 1;
243 $self->{person_id} = $login_info->{person_id};
244 $self->{cookie_string} = $login_info->{cookie_string};
245 $self->{user_prefs} = $login_info->{user_prefs};
247 if($login_warning) { $login_warning .= "<br>" }
249 $self->{just_logged_in} = $just_logged_in;
250 $self->{login_warning} = $login_warning;
251 if($extra_notification) { $login_warning = $extra_notification }
253 if($self->{person_id}){
254 #makes sure that cookie and database user_pref string are current and agreeable on initial login or when loading any page. This is important when the user changes a preference, then jumps to a page in which UserPrefs ISN'T used, so the database will stil be updated with the new client cookie string.
255 my $prefh = CXGN::UserPrefs->new( $self->{dbh}, $self->{person_id} );
256 $self->{user_prefs} = $prefh->get_user_pref_string();
259 #logout is done last since we want to sync up the userprefs that were set before the logout button was clicked
260 if($self->{logout}){
261 $self->logout();
266 return $login_warning;
270 ### CXGN::Page over-riding functions #############
271 sub get_system_message {
272 my $self = shift;
273 my $system_message='';
274 if(my $message_file=$self->{vhost_object}->get_conf('system_message_file'))
276 my $message_text=CXGN::Tools::File::file_contents($message_file);
277 $system_message.="<span class=\"developererrorbox\">$message_text</span><br />";
279 unless($self->{vhost_object}->get_conf('production_server'))
281 my $connection_test=CXGN::DB::Connection->new_no_connect();
282 $system_message.="<span class=\"developererrorbox\">Viewing the ".$connection_test->dbname." database on ".$connection_test->dbhost.", ".$connection_test->dbbranch." branch</span>\n";
284 else { die("This site is not ready to go live yet!");}
286 return $system_message;
289 sub get_header {
290 my $self = shift;
291 # $self->{request}->send_http_header("text/html");
292 if($self->{content_title}){ $self->{page_title} .= ": " . $self->{content_title} }
294 my $system_message = $self->get_system_message();
295 my $login_warning = $self->get_login_info();
297 my $login_panel = $self->{page_object}->login_panel($self->{gene}, $self->{searchQuery});
299 my $html_head = $self->html_head();
301 my $user_bar = $self->{page_object}->user_bar
303 $self->{gene}, $self->{searchQuery}, $self->{just_logged_in}, $self->{person_id}, $self->{error404}
305 my $body_tag = "<body>";
307 my $notifier = CXGN::Page::Widgets::notifier();
309 if($self->{content_title} =~ /Home/i) { $body_tag = "<body onload='document.getElementById(\"query_focus\").focus()'>" }
310 return<<HTML;
311 <html>
312 $html_head
313 $body_tag
314 <center>
315 <div id='userbar_container' style='width:100%'>
316 <table style='width:100%; margin-bottom:5px;'>
317 <tr>
318 <td style='text-align:left'>
319 $system_message
320 </td>
321 <td style='text-align:right'>
322 $user_bar
323 </td>
324 </tr>
325 </table>
326 </div>
327 <span id='login_warning' style='color:#dd4444; font-size:1.05em'>$login_warning</span>
328 $login_panel
329 </center>
330 <noscript>
331 <center>
332 <span style='font-size:0.9em; color:#990000'>
333 <a href='/documents/howtojs.html' style='color:#993355'><b>Javascript</b></a> (and cookies) must be enabled for logging in, hotlists, and dynamic effects
334 </span>
335 </center>
336 </noscript>
337 <center>
338 $notifier
339 </center>
341 <!--/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/| BEGIN PAGE CONTENT |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-->
343 HTML
346 sub header {
347 my $self = shift;
348 ($self->{page_title}, $self->{content_title}, $self->{extra_head_stuff}) = @_;
349 unless($self->{page_title})
351 $self->{page_title}=$self->{name};
353 print $self->get_header();
356 sub footer {
357 my $self = shift;
358 my $extra_footer_stuff = shift;
359 print $self->get_footer($extra_footer_stuff);
362 sub get_footer {
363 my $self = shift;
364 my $extra_footer_stuff = shift;
365 $extra_footer_stuff||='';
366 return <<HTML;
367 </body>
368 <head>
369 <META HTTP-EQUIV="cache-control" CONTENT="no-store">
370 <META HTTP-EQUIV="cache-control" CONTENT="no-cache">
371 $extra_footer_stuff
372 </head>
373 </html>
374 HTML
377 sub jsan_render_includes {
378 my ($self) = @_;
379 return join("\n",map { qq|<script language="JavaScript" src="$_" type="text/javascript"></script>| } $self->_jsan->uris);
382 sub html_head
384 my $self=shift;
385 $self->jsan_use(qw|CXGN.Base CXGN.UserPrefs CXGN.Effects CXGN.Hotlist CXGN.secretary|);
386 my $jsanh = $self->_jsan();
387 my $script_src = $self->jsan_render_includes();
388 my $pathex = $jsanh->_class_to_file("CXGN.Effects");
390 #my $script_src = $self->jsan_render_includes();
391 return<<HTML;
393 <head><title>$self->{page_title}</title>
395 <link rel="stylesheet" href="/documents/inc/secretary.css" TITLE="Secretary Default" TYPE="text/css" />
396 <link rel="icon" href="favicon.ico" type="image/x-icon" />
397 <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
399 <META HTTP-EQUIV="cache-control" CONTENT="no-store" />
400 <META HTTP-EQUIV="cache-control" CONTENT="no-cache" />
402 <script language='javascript'>
403 var JSAN = {};
404 JSAN.use = function () {};
406 //Global page variables for all scripts
407 var thispage = location.href;
408 var agi = '$self->{gene}';
409 var query = '$self->{searchQuery}';
410 var user_id = '$self->{person_id}';
411 var cookie_string = '$self->{cookie_string}';
412 var user_pref_string = '$self->{user_prefs}';
413 var just_logged_in = '$self->{just_logged_in}';
414 if(just_logged_in == '1') location.href = thispage;
416 </script>
417 $script_src
418 <!--transitional script
419 $pathex
420 <script language='javascript' src='/documents/inc/secretary.js'></script>
422 </head>
424 HTML
427 ## Secretary page elements ##################
428 sub hotlist_button {
429 my $self = shift;
430 my $agi = shift;
431 my $buttonText = "";
432 my $enum = 0;
433 my $i = 0;
434 while($self->{hotlist_button_ids}->{"$agi:$i"}){
435 $i++;
437 $enum = $i;
438 $self->{hotlist_button_ids}->{"$agi:$enum"} = 1;
440 if(!$self->{hotlist_content}) { $self->set_hotlist() }
441 if($self->{validUser}){
442 my $in_hotlist = 0;
443 foreach(@{$self->{hotlist_content}}){
444 if ($_ eq $agi) { $in_hotlist = 1 }
446 if($in_hotlist) {
447 $buttonText .= <<HTML;
448 <img id='hotlistButton:$agi:$enum:imgAdd' src='/documents/img/secretary/hotlist_add.png' style='display:none' border=0 \>
449 <img id='hotlistButton:$agi:$enum:imgRemove' src='/documents/img/secretary/hotlist_remove.png' style='display:inline' border=0 \>
450 <a href='#' id='hotlistButton:$agi:$enum'
451 onclick='Hotlist.remove(this.id, \"$agi\"); return false;'
452 style='text-decoration:none'>Remove from Hotlist</a>
453 HTML
455 else {
456 $buttonText .= <<HTML;
457 <img id='hotlistButton:$agi:$enum:imgAdd' src='/documents/img/secretary/hotlist_add.gif' style='display:inline' border=0 \>
458 <img id='hotlistButton:$agi:$enum:imgRemove' src='/documents/img/secretary/hotlist_remove.gif' style='display:none' border=0 \>
459 <a href='#' id='hotlistButton:$agi:$enum'
460 onclick='Hotlist.add(this.id, \"$agi\");return false;'
461 style='text-decoration:none'>Add to Hotlist</a>
462 HTML
464 $buttonText .= "&nbsp;<span id='hotlistWait:$agi:$enum' style='display:none'>...</span>";
466 else {
467 $buttonText .= <<HTML
468 <img src='/documents/img/secretary/hotlist_add.gif' border=0 \>
469 <a href='#' style='text-decoration:none'
470 onMouseOver='document.getElementById("hotlistGuest:$agi:$enum").style.display = "inline";'
471 onMouseOut='document.getElementById("hotlistGuest:$agi:$enum").style.display = "none";'
472 onclick='show_login();'>Add to Hotlist</a>&nbsp;
474 <span id='hotlistGuest:$agi:$enum' style='display:none;color:#aa3333' > Login to use hotlists</span>
475 HTML
477 return $buttonText;
480 sub navigation_info {
481 my $self = shift;
482 my $extraHREF = shift;
483 my $querysize = $self->{querySize};
484 my $lb = $self->{leftBound};
485 my $searchquery = $self->{searchQuery};
486 my $referenceGene = $self->{referenceGene};
487 my $physicalMode = $self->{physicalMode};
488 my $prevQuery = $self->{prevQuery};
489 my $prevLB = $self->{prevLB};
491 if(!$physicalMode){
492 my $rb = $lb + 19;
493 if((defined $querysize) && ($querysize < 20 || ($lb + 19) > $querysize)){
494 $rb = $querysize;
496 my $navinfo = "<span style='font-size:1em'>Results <b>$lb</b> - <b>$rb</b> of ";
497 if(defined $querysize) {$navinfo .= "<b>$querysize</b><br>"}
498 else {$navinfo .= "<b>200+</b><br>"}
499 return $navinfo;
501 else {
503 my %lowerbounds = ( 1=>10, 2=>10, 3=>10,4=>0,5=>10,M=>0,C=>0);
504 my %upperbounds = (1=>809, 2=>481, 3=>666, 4=>401, 5=>676, M=>14, C=>13);
505 my ($currentpos) = $searchquery =~ /AT[1-5MC]G(\d{1,3})/;
506 my ($refpos) = $referenceGene =~ /AT[1-5MC]G(\d{1,3})/;
507 my ($chromchar) = $searchquery =~ /AT([1-5MC])G/;
508 my $lower = $lowerbounds{$chromchar};
509 my $upper = $upperbounds{$chromchar};
510 $lower = int($lower / 5) * 5;
511 $currentpos = zeropadleft(int(zeropadright($currentpos)));
512 $refpos = zeropadleft(int($refpos));
513 my $navinfo = "<span style='font-size:1em'><b>$querysize</b> genes at the prefix <b>AT$chromchar" . "G$currentpos</b>";
514 $navinfo .= "&nbsp; &nbsp;<b style='letter-spacing:0px'>(";
515 my $i = $lower;
516 while ($i <= $upper){
517 my $pos = $i;
518 if(abs($pos-$currentpos)<3 || (abs($upper-$currentpos)<3 && abs($upper-$pos)<5)) {
519 $pos = $currentpos;
520 $navinfo .= "<span style='color:white;background-color:#000088'>|</span>";
522 elsif(abs($pos-$refpos)<3){
523 $navinfo .= "<a title='" . zeropadleft($refpos) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft($refpos);
524 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
525 $navinfo .= " style='text-decoration:none; background-color:#00AA00;font-weight:bold;color:white'>|</a>";
527 elsif(abs($upper-$pos)<5){
528 $navinfo .= "<a title='" . zeropadleft($upper) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft($upper);
529 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
530 $navinfo .= " style='text-decoration:none;font-weight:bold;'>|</a>";
532 else{
533 $navinfo .= "<a title='" . zeropadleft($pos) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft($pos);
534 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
535 $navinfo .= " style='font-weight:bold;text-decoration:none'>|</a>";
537 $i += 5;
540 $navinfo .= ")</b></span>";
541 return $navinfo;
545 sub navigation_control {
546 my $self = shift;
547 my $prevReplace = shift;
548 my $nextReplace = shift;
549 my $extraHREF = shift;
550 $extraHREF ||= "";
551 my $lb = $self->{leftBound};
552 my $query = $self->{searchQuery};
553 my $nocheck = $self->{noCheck};
554 my $referenceGene = $self->{referenceGene};
555 my $physicalMode = $self->{physicalMode};
556 my $querysize = $self->{querySize};
557 my $prevQuery = $self->{prevQuery};
558 my $prevLB = $self->{prevLB};
559 my $navbar = "<span class='navbar'>";
561 if(!$physicalMode){ #standard search-relevance navigation
562 my $nextbound = 0;
564 if($lb>1) {
565 if(defined $querysize){
566 $nextbound = $lb - 20;
567 if($nextbound != 1 || 1) {
568 $navbar .= "<a href='query.pl?query=$query&lb=1&qsize=$querysize&nocheck=$nocheck&prevQ=$prevQuery&prevLB=${prevLB}${extraHREF}'>&laquo; First</a> &nbsp;";
570 else {
571 $navbar .= "<span style='color:grey'><u>&laquo; First</u> &nbsp;</span>";
573 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>&#8249; Prev</a> &nbsp;";
575 else{
576 $nextbound = $lb - 20;
577 if($nextbound != 1 || 1) { ##still deciding whether to grey-out if unnecessary
578 $navbar .= "<a href='query.pl?query=$query&lb=1&nocheck=${nocheck}${extraHREF}'>&laquo; First</a> &nbsp;";
580 else {
581 $navbar .= "<span style='color:grey'><u>&laquo; First</u> &nbsp;</span>";
583 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&nocheck=${nocheck}${extraHREF}'>&#8249; Prev</a> &nbsp;";
587 else {
588 $navbar .= "<span style='color:grey'><u>&laquo; First</u> &nbsp;<u>&#8249; Prev</u> &nbsp;</span>";
591 if((!defined $querysize) || (($querysize - $lb) >= 20)){
592 $nextbound = $lb + 20;
593 my $lastbound;
594 if(defined $querysize) {
595 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>Next &#8250;</a> &nbsp;";
597 else {
598 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&nocheck=${nocheck}${extraHREF}'>Next &#8250;</a> &nbsp;"
600 if(!defined $querysize) {$lastbound = $lb + 200 }
601 elsif($querysize%20) {$lastbound = $querysize - $querysize%20 + 1 }
602 else {$lastbound = $querysize - 19 }
604 if(defined $querysize) {
605 if($lastbound != $nextbound || 1){
606 $navbar .= "<a href='query.pl?query=$query&lb=$lastbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>Last &raquo;</a>";
608 else {
609 $navbar .= "<span style='color:grey'><u>Last &raquo;</u></span>";
612 else {
613 $navbar .= "<a href='query.pl?query=$query&lb=$lastbound&nocheck=${nocheck}${extraHREF}'>Jump &raquo;</a>";
616 else {
617 $navbar .= "<span style='color:grey'><u>Next &#8250;</u> &nbsp;<u>Last &raquo;</u></span>";
621 #gene pseudo-physical navigation
622 else {
624 ###Locus Boundaries####found by ordering locii in database#################
625 # Lower Bounds:
626 # 1:01010
627 # 2:01031
628 # 3:01010
629 # 4:00010
630 # 5:01010
631 # M:00010
632 # C:00020
634 # Upper Bounds
635 # 1:80990
636 # 2:48160
637 # 3:66658
638 # 4:40100
639 # 5:67640
640 # M:01410
641 # C:01310
642 my %lowerbounds = (1=>10, 2=>10, 3=>10,4=>0,5=>10,M=>0,C=>0);
643 my %upperbounds = (1=>809, 2=>481, 3=>666, 4=>401, 5=>676, M=>14, C=>13);
645 my ($chrom) = $query =~ /(AT[1-5MC]G)/;
646 my ($chromchar) = $query =~ /AT([1-5MC])G/;
647 my ($location) = $query =~ /AT[1-5MC]G(\d{1,3})/;
649 if(length($location)<1) { $location .= "000" }
650 elsif(length($location) == 1) { $location .= "00" }
651 elsif(length($location) ==2) { $location .= "0" }
652 $location = int($location);
653 my $jumpleft = $location - 10;
654 my $jumpright = $location + 10;
655 my $next = $location + 1;
656 my $prev = $location - 1;
658 if($location > ($lowerbounds{$chromchar}+10)) {
659 $navbar .= "<a href='query.pl?query=$chrom" . zeropadleft($jumpleft) . "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>&laquo; (x10)</a>&nbsp;&nbsp;";
661 else {
662 $navbar .= "<span style='color:grey'><u>&laquo; (x10)</u></span>&nbsp;&nbsp;";
664 if($location > $lowerbounds{$chromchar}) {
665 $navbar .= "<a href='query.pl?query=";
666 if($prevReplace){
667 $navbar .= $prevReplace;
669 else {
670 $navbar .= $chrom . zeropadleft($prev);
672 $navbar .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>&#8249; Left</a>&nbsp;&nbsp;";
674 else {
675 $navbar .= "<span style='color:grey'><u>&#8249; Left</u></span>&nbsp;&nbsp;";
677 if($location < $upperbounds{$chromchar}) {
678 $navbar .= "<a href='query.pl?query=";
679 if($nextReplace){
680 $navbar .= $nextReplace;
682 else {
683 $navbar .= $chrom . zeropadleft($next);
685 $navbar .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>Right &#8250;</a>&nbsp;&nbsp;";
687 else {
688 $navbar .= "<span style='color:grey'><u>Right &#8250;</u></span>&nbsp;&nbsp;";
690 if($location < ($upperbounds{$chromchar}-10)) {
691 $navbar .= "<a href='query.pl?query=$chrom" . zeropadleft($jumpright) . "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>(x10) &raquo;</a> ";
693 else {
694 $navbar .= "<span style='color:grey'><u>(x10) &raquo;</u></span>";
697 $navbar .= "</span>";
698 return $navbar;
701 ## Helper routines ##########################
702 sub zeropadleft {
703 my ($location) = @_;
705 if(length($location)==1) {
706 return "00" . $location;
708 elsif(length($location)==2) {
709 return "0" . $location;
711 else {
712 return "" . $location;
716 sub zeropadright {
717 my ($location) = @_;
719 if(length($location)==1) {
720 return $location . "00";
722 elsif(length($location)==2) {
723 return $location . "0";
725 else {
726 return $location . "";
732 1;#do not remove