How can I catch a `shift`-`key` combination with Win32::Console::Input? - perl

How can I catch a shift-some-key combination with this script?
When I press the Arrow-keys I get what I expect, but when I press shift-tab it doesn't return the KEY_BTAB value.
use warnings;
use 5.12.0;
use Win32::Console qw(STD_INPUT_HANDLE ENABLE_MOUSE_INPUT);
use constant {
RIGHT_ALT_PRESSED => 0x0001,
LEFT_ALT_PRESSED => 0x0002,
RIGHT_CTRL_PRESSED => 0x0004,
LEFT_CTRL_PRESSED => 0x0008,
SHIFT_PRESSED => 0x0010,
VK_LEFT => 0x25,
VK_UP => 0x26,
VK_RIGHT => 0x27,
VK_DOWN => 0x28,
VK_TAB => 0x09,
};
use constant SHIFTED_MASK =>
RIGHT_ALT_PRESSED |
LEFT_ALT_PRESSED |
RIGHT_CTRL_PRESSED |
LEFT_CTRL_PRESSED |
SHIFT_PRESSED;
my %d = (
KEY_DOWN => 258,
KEY_UP => 259,
KEY_LEFT => 260,
KEY_RIGHT => 261,
KEY_BTAB => 353,
);
my $con_in = Win32::Console->new(STD_INPUT_HANDLE);
$con_in->Mode(ENABLE_MOUSE_INPUT);
while ( 1 ) {
my $key = getch();
say "<$key>";
last if $key == 113;
}
sub getch {
my #event = $con_in->Input();
my $event_type = shift( #event );
if ( defined $event_type and $event_type == 1 ) {
my ( $key_down, $repeat_c, $vkcode, $vsccode, $char, $ctrl_ks ) = #event;
if ( $char ) {
return $char;
}
else {
if ( $vkcode == VK_UP and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
return $d{KEY_UP};
}
elsif ( $vkcode == VK_DOWN and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
return $d{KEY_DOWN};
}
elsif ( $vkcode == VK_RIGHT and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
return $d{KEY_RIGHT};
}
elsif ( $vkcode == VK_LEFT and ( $ctrl_ks & SHIFTED_MASK ) == 0 ) {
return $d{KEY_LEFT};
}
elsif ( $vkcode == VK_TAB and $ctrl_ks == SHIFT_PRESSED ) {
return $d{KEY_BTAB}; # <--
}
else {
say "beep";
}
}
}
}
Output when I press shift and tab:
beep
<1>
<9>
<9>
beep
<1>
After editing the getch routine this way
sub getch {
my #event = $con_in->Input();
my $event_type = shift( #event );
if ( defined $event_type and $event_type == 1 ) {
my ( $key_down, $repeat_count, $virtual_keycode, $virtual_scancode, $char, $ctrl_key_state ) = #event;
if ( $char ) {
if ( $key_down ) {
return $char for $repeat_count;
}
}
else {
if ( $virtual_keycode == VK_UP and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
if ( $key_down ) {
return $d{KEY_UP} for $repeat_count;
}
}
elsif ( $virtual_keycode == VK_DOWN and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
if ( $key_down ) {
return $d{KEY_DOWN} for $repeat_count;
}
}
elsif ( $virtual_keycode == VK_RIGHT and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
if ( $key_down ) {
return $d{KEY_RIGHT} for $repeat_count;
}
}
elsif ( $virtual_keycode == VK_LEFT and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
if ( $key_down ) {
return $d{KEY_LEFT} for $repeat_count;
}
}
elsif ( $virtual_keycode == VK_TAB and ( $ctrl_key_state & SHIFTED_MASK ) == SHIFT_PRESSED ) {
if ( $key_down ) {
return $d{KEY_BTAB} for $repeat_count;
}
}
else {
say "beep";
}
}
}
}
I get this output:
beep
<1>
<9>
<0>
beep
<1>

When I run your code, $ctrl_ks has the value 48 (0x0030) when I hit Shift+Tab, and 32 (0x0020) when Shift is released. I think the check you want to make is
elsif ($vkcode==VK_TAB and ($ctrl_ks & SHIFT_PRESSED)==SHIFT_PRESSED) {
return $d{KEY_BTAB};

Firstly, $char is set to 9, so you never get to your check. Move the if ($char) check to somewhere more appropriate.
Secondly, your check is wrong. The following won't work if, say, Caps Lock is on.
elsif ( $vkcode == VK_TAB and $ctrl_ks == SHIFT_PRESSED )
You should only check the flags you are interested in.
elsif ($vkcode==VK_TAB and ( $ctrl_ks & SHIFTED_MASK ) == SHIFT_PRESSED)
Finally, sometimes you only get notified once for multiple presses. That is signaled by $repeat_count. You ignore this, so you potentially ignore keys.
You try to handle $repeat_count in the second snippet, but fail miserably. Part of the problem is you copied for $repeat_count from my other answer when it should be for 1..$repeat_count, and the other problem is that you only return one value even if $repeat_count is larger than one.
my #kbd_queue;
sub getch {
my #event;
if (#kbd_queue) {
#event = ( 1, #{ pop #kbd_queue } );
} else {
#event = $con_in->Input();
}
my $event_type = shift( #event );
if ( defined $event_type and $event_type == 1 ) {
my ( $key_down, $repeat_count, $virtual_keycode, $virtual_scancode, $char, $ctrl_key_state ) = #event;
return -1 if !$key_down;
if ( $virtual_keycode == VK_UP and ( $ctrl_key_state & SHIFTED_MASK ) == 0 ) {
push #kbd_queue, \#event for 2..$repeat_count;
return $d{KEY_UP};
}
...
elsif ( $virtual_keycode == VK_TAB and ( $ctrl_key_state & SHIFTED_MASK ) == SHIFT_PRESSED ) {
push #kbd_queue, \#event for 2..$repeat_count;
return $d{KEY_BTAB};
}
elsif ( $char ) {
push #kbd_queue, \#event for 2..$repeat_count;
return $char;
}
else {
say "beep";
}
}
}
You should convert this into something table-driven.

Related

Test two boolean values with AND operator in Perl

I want to test two boolean values in Perl.
In case both are 0 do x, in case not do y.
For example:
$var1 = 0; false in Perl
$var2 = 0; false in Perl
if($var1==0 && var2==0)
{
x //both are false
}
else
{
y //both are true
}
if ( $var1 && $var2 ) {
# Both are true.
}
elsif ( !$var1 && !$var2 ) {
# Both are false.
}
or
if ( !( $var1 xor $var2 ) ) {
# Both are true or both are false.
if ( $var1 ) {
# Both are true.
} else {
# Both are false.
}
}
or
if ( $var1 ) {
if ( $var2 ) {
# Both are true.
}
} else {
if ( !$var2 ) {
# Both are false.
}
}
So … assuming you want y only when both $var1 and $var2 are true:
if (!$var1 and !$var2) {
# do x
} elsif ($var1 and $var2) {
# do y
}
But there TIMTOWTDI. Because Perl.

CKE change blockqoute style

When i use this code in CKE Editor:
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
can i change this:
Image 1
to this:
Image 2
Problem: The PN Messenger in our CMS reply the last message with blockquote in blockquote. And with several replies this is very confusing.
Your Code
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
Updated
<blockquote>test</blockquote>
<blockquote>test</blockquote>
You want to display every blockquote in a new line. Try this
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
<head>
<title>Page Title</title>
</head>
<body>
<blockquote>test
<blockquote>test</blockquote>
</blockquote>
</body>
I insert your code here (line 24) Is that correct? But it do not change.
1/**
* #license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
( function() {
function noBlockLeft( bqBlock ) {
for ( var i = 0, length = bqBlock.getChildCount(), child; i < length && ( child = bqBlock.getChild( i ) ); i++ ) {
if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
return false;
}
return true;
}
var commandObject = {
exec: function( editor ) {
var state = editor.getCommand( 'blockquote' ).state,
selection = editor.getSelection(),
range = selection && selection.getRanges()[ 0 ];
if ( !range )
return;
// Rakibul Islam
var array = document.getElementsByTagName('blockquote');
for(var i = 0; i < array.length; i++) {
array[i].savedContent = array[i].innerHTML;
array[i].style.margin = 0;
}
var bookmarks = selection.createBookmarks();
// Kludge for https://dev.ckeditor.com/ticket/1592: if the bookmark nodes are in the beginning of
// blockquote, then move them to the nearest block element in the
// blockquote.
if ( CKEDITOR.env.ie ) {
var bookmarkStart = bookmarks[ 0 ].startNode,
bookmarkEnd = bookmarks[ 0 ].endNode,
cursor;
if ( bookmarkStart && bookmarkStart.getParent().getName() == 'blockquote' ) {
cursor = bookmarkStart;
while ( ( cursor = cursor.getNext() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkStart.move( cursor, true );
break;
}
}
}
if ( bookmarkEnd && bookmarkEnd.getParent().getName() == 'blockquote' ) {
cursor = bookmarkEnd;
while ( ( cursor = cursor.getPrevious() ) ) {
if ( cursor.type == CKEDITOR.NODE_ELEMENT && cursor.isBlockBoundary() ) {
bookmarkEnd.move( cursor );
break;
}
}
}
}
var iterator = range.createIterator(),
block;
iterator.enlargeBr = editor.config.enterMode != CKEDITOR.ENTER_BR;
if ( state == CKEDITOR.TRISTATE_OFF ) {
var paragraphs = [];
while ( ( block = iterator.getNextParagraph() ) )
paragraphs.push( block );
// If no paragraphs, create one from the current selection position.
if ( paragraphs.length < 1 ) {
var para = editor.document.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ),
firstBookmark = bookmarks.shift();
range.insertNode( para );
para.append( new CKEDITOR.dom.text( '\ufeff', editor.document ) );
range.moveToBookmark( firstBookmark );
range.selectNodeContents( para );
range.collapse( true );
firstBookmark = range.createBookmark();
paragraphs.push( para );
bookmarks.unshift( firstBookmark );
}
// Make sure all paragraphs have the same parent.
var commonParent = paragraphs[ 0 ].getParent(),
tmp = [];
for ( var i = 0; i < paragraphs.length; i++ ) {
block = paragraphs[ i ];
commonParent = commonParent.getCommonAncestor( block.getParent() );
}
// The common parent must not be the following tags: table, tbody, tr, ol, ul.
var denyTags = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 };
while ( denyTags[ commonParent.getName() ] )
commonParent = commonParent.getParent();
// Reconstruct the block list to be processed such that all resulting blocks
// satisfy parentNode.equals( commonParent ).
var lastBlock = null;
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
while ( !block.getParent().equals( commonParent ) )
block = block.getParent();
if ( !block.equals( lastBlock ) )
tmp.push( block );
lastBlock = block;
}
// If any of the selected blocks is a blockquote, remove it to prevent
// nested blockquotes.
while ( tmp.length > 0 ) {
block = tmp.shift();
if ( block.getName() == 'blockquote' ) {
var docFrag = new CKEDITOR.dom.documentFragment( editor.document );
while ( block.getFirst() ) {
docFrag.append( block.getFirst().remove() );
paragraphs.push( docFrag.getLast() );
}
docFrag.replace( block );
} else {
paragraphs.push( block );
}
}
// Now we have all the blocks to be included in a new blockquote node.
var bqBlock = editor.document.createElement( 'blockquote' );
bqBlock.insertBefore( paragraphs[ 0 ] );
while ( paragraphs.length > 0 ) {
block = paragraphs.shift();
bqBlock.append( block );
}
} else if ( state == CKEDITOR.TRISTATE_ON ) {
var moveOutNodes = [],
database = {};
while ( ( block = iterator.getNextParagraph() ) ) {
var bqParent = null,
bqChild = null;
while ( block.getParent() ) {
if ( block.getParent().getName() == 'blockquote' ) {
bqParent = block.getParent();
bqChild = block;
break;
}
block = block.getParent();
}
// Remember the blocks that were recorded down in the moveOutNodes array
// to prevent duplicates.
if ( bqParent && bqChild && !bqChild.getCustomData( 'blockquote_moveout' ) ) {
moveOutNodes.push( bqChild );
CKEDITOR.dom.element.setMarker( database, bqChild, 'blockquote_moveout', true );
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
var movedNodes = [],
processedBlockquoteBlocks = [];
database = {};
while ( moveOutNodes.length > 0 ) {
var node = moveOutNodes.shift();
bqBlock = node.getParent();
// If the node is located at the beginning or the end, just take it out
// without splitting. Otherwise, split the blockquote node and move the
// paragraph in between the two blockquote nodes.
if ( !node.getPrevious() )
node.remove().insertBefore( bqBlock );
else if ( !node.getNext() )
node.remove().insertAfter( bqBlock );
else {
node.breakParent( node.getParent() );
processedBlockquoteBlocks.push( node.getNext() );
}
// Remember the blockquote node so we can clear it later (if it becomes empty).
if ( !bqBlock.getCustomData( 'blockquote_processed' ) ) {
processedBlockquoteBlocks.push( bqBlock );
CKEDITOR.dom.element.setMarker( database, bqBlock, 'blockquote_processed', true );
}
movedNodes.push( node );
}
CKEDITOR.dom.element.clearAllMarkers( database );
// Clear blockquote nodes that have become empty.
for ( i = processedBlockquoteBlocks.length - 1; i >= 0; i-- ) {
bqBlock = processedBlockquoteBlocks[ i ];
if ( noBlockLeft( bqBlock ) )
bqBlock.remove();
}
if ( editor.config.enterMode == CKEDITOR.ENTER_BR ) {
var firstTime = true;
while ( movedNodes.length ) {
node = movedNodes.shift();
if ( node.getName() == 'div' ) {
docFrag = new CKEDITOR.dom.documentFragment( editor.document );
var needBeginBr = firstTime && node.getPrevious() && !( node.getPrevious().type == CKEDITOR.NODE_ELEMENT && node.getPrevious().isBlockBoundary() );
if ( needBeginBr )
docFrag.append( editor.document.createElement( 'br' ) );
var needEndBr = node.getNext() && !( node.getNext().type == CKEDITOR.NODE_ELEMENT && node.getNext().isBlockBoundary() );
while ( node.getFirst() )
node.getFirst().remove().appendTo( docFrag );
if ( needEndBr )
docFrag.append( editor.document.createElement( 'br' ) );
docFrag.replace( node );
firstTime = false;
}
}
}
}
selection.selectBookmarks( bookmarks );
editor.focus();
},
refresh: function( editor, path ) {
// Check if inside of blockquote.
var firstBlock = path.block || path.blockLimit;
this.setState( editor.elementPath( firstBlock ).contains( 'blockquote', 1 ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
},
context: 'blockquote',
allowedContent: 'blockquote',
requiredContent: 'blockquote'
};
CKEDITOR.plugins.add( 'blockquote', {
// jscs:disable maximumLineLength
lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
// jscs:enable maximumLineLength
icons: 'blockquote', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
init: function( editor ) {
if ( editor.blockless )
return;
editor.addCommand( 'blockquote', commandObject );
editor.ui.addButton && editor.ui.addButton( 'Blockquote', {
label: editor.lang.blockquote.toolbar,
command: 'blockquote',
toolbar: 'blocks,10'
} );
}
} );
} )();

Where can I change the calendar start date to Sunday from Monday

I am trying to make this calendar start on Sunday instead of Monday. I've looked through all the help files I can Google on this topic & cannot find any that have any lines of code that I can reference. Am I missing some? Or can some one point me to the point I need to change in order to make it start on Sunday instead of Monday?
<?php
/**
* Copyright (C) 2006-2014 Center Stage Software
* 1191 Luxton Street, Seaside, California 93955
* All Rights Reserved
*/
// ensure that this file is called up by another file
defined('_VALID_ACCESS') or die('Direct access of this file is prohibited.');
/**
* renderEventCalendar - Creates a calendar display of events for one month.
*
* #global (Array) $cstage
*/
function renderEventCalendar() {
global $cstage, $path;
// Result variable.
$calendar = '';
// Calculate first and last day of the month.
$firstDayTimestamp = strtotime($_SESSION[SYS_USER]['search']['Month'].'-01 07:00:00');
list($year,$month) = explode("-",$_SESSION[SYS_USER]['search']['Month']);
if( $month == 12 ) {
$month = 00;
$year += 1;
}
$lastDayTimestamp = strtotime("$year-".($month+1).'-01 08:00:00') - 86400; // minus 24 hours and add 1-day-light-savings-hour
// TODO: Remove the following calendar debugging code.
// print "<h1>first=$firstDayTimestamp; last=$lastDayTimestamp</h1>";
// Build query to retrieve entries for that month.
$aryEvents = array();
$extraSqlFields = '';
$extraSqlJoins = '';
$extraSqlConditions = '';
if( !empty($_SESSION[SYS_USER]['domain']) ) {
$extraSqlJoins .= 'LEFT JOIN "domainlist" AS "domain2" ON "domain2"."master_id" = "master"."master_id" ';
$extraSqlConditions .= sprintf('AND "domain2"."domain"=\'%s\' '
,$_SESSION[SYS_USER]['domain']
);
}
if( !empty($_SESSION[SYS_USER]['search']['Theatre']) ) {
$extraSqlConditions .= sprintf('AND "master"."theatre"=\'%s\' '
,$_SESSION[SYS_USER]['search']['Theatre']
);
}
if( !empty($_SESSION[SYS_USER]['search']['Location']) ) {
$extraSqlConditions .= sprintf('AND "master"."location"=\'%s\' '
,$_SESSION[SYS_USER]['search']['Location']
);
}
if( !empty($_SESSION[SYS_USER]['search']['EventType']) ) {
$extraSqlConditions .= sprintf('AND "master"."event_types_id"=\'%s\' '
,$_SESSION[SYS_USER]['search']['EventType']
);
}
if( !empty($_SESSION[SYS_USER]['search']['Month']) ) {
$extraSqlConditions .= sprintf('AND "shows"."show_date" >= \'%s\' AND "shows"."show_date" <= \'%s\' '
,date('Y-m-d',$firstDayTimestamp)
,date('Y-m-d',$lastDayTimestamp)
);
}
if( $cstage['rttEnable'] ) {
$extraSqlFields .= ', "rtt"."label" AS "rtt_label"';
$extraSqlJoins .= 'LEFT JOIN "rtt" ON "master"."rtt_id" = "rtt"."rtt_id" ';
} else {
$extraSqlConditions .= 'AND "master"."rtt_id"=0 ';
}
if( $cstage['enableEventPriceListTest'] ) {
$aryGroups = array($cstage['tixDomain']);
if( !empty($_SESSION[SYS_USER]['info']) && count($_SESSION[SYS_USER]['info']['groupDomains']) > 0 ) {
$aryGroups = array_merge($aryGroups, $_SESSION[SYS_USER]['info']['groupDomains']);
}
$extraSqlFields .= ', GROUP_CONCAT(DISTINCT "prices"."prices_id" SEPARATOR \';\') AS "priceidlist"';
$extraSqlFields .= ', GROUP_CONCAT(DISTINCT CONCAT("price_category"."label",\' \', "prices"."printed_as") SEPARATOR \', \') AS "pricelist"';
$extraSqlJoins .= ' INNER JOIN "price_category" ON "price_category"."master_id" = "master"."master_id" OR "price_category"."master_id"=0 INNER JOIN "prices" ON "price_category"."price_category_id" = "prices"."price_category_id" INNER JOIN "price_category_domain" ON "price_category_domain"."price_category_id" = "price_category"."price_category_id"';
$extraSqlConditions .= sprintf('AND ("price_category"."master_id"="shows"."master_id" OR "price_category"."master_id"=0) AND ("prices"."shows_id"="shows"."shows_id" OR "prices"."shows_id"=0) AND "prices"."onsale"<=CONCAT("shows"."show_date", \' 00:00:00\') AND ("prices"."offsale">=CONCAT("shows"."show_date", \' 23:59:59\') OR "prices"."offsale" IS NULL) AND "price_category_domain"."domain" IN (\'%s\') '
,implode('\',\'', $aryGroups)
);
}
$now = date('Y-m-d H:i:s');
$query = sprintf('SELECT "master".*, "shows"."show_date", "shows"."show_time", "shows"."onsale" AS "min_onsale"%s FROM "shows" INNER JOIN "master" ON "master"."master_id" = "shows"."master_id" INNER JOIN "domainlist" ON "domainlist"."master_id" = "master"."master_id" %s WHERE "shows"."offsale" >= \'%s\' AND ("shows"."wt_offsale"=\'\' OR "shows"."wt_offsale"=0) AND "domainlist"."domain"=\'%s\' %s GROUP BY "shows"."shows_id"'
,$extraSqlFields
,$extraSqlJoins
,$now
,$cstage['tixDomain']
,$extraSqlConditions
);
$keyEventCalendar = 'eventCalendar_'.$cstage['dbDatabase'].sha1($query);
if( !empty($cstage['cache']) ) {
// Is the record cached to save on slow database connections?
$calendar = $cstage['cache']->load($keyEventCalendar);
}
if( !$calendar ) {
$result = $cstage['pdo']->query( $query );
while( $show = $result->fetch( PDO::FETCH_ASSOC ) ) {
$show['domainlist'] = $cstage['tixDomain'].';'.$_SESSION[SYS_USER]['domain'];
$show['comingSoon'] = false;
if( $now < $show['min_onsale'] ) {
$show['comingSoon'] = true;
}
if( $show['rtt_id'] > 0 ) {
$show['master_id'] = trim($show['rtt_label']).trim($show[]);
if( !$cstage['rttEnable'] ) {
$show = array();
}
}
$path->inc_once('_includes/libf_okEvent.php');
if( okEvent($show) ) {
// Save data into array for usage later
$extraSort = $cstage['useEventSortOrder'] === true ? sprintf('%05d',$show['sortorder']) : '';
$aryEvents[$show['show_date']][date('H:i',strtotime($show['show_time'])).$extraSort.$show['showname'].$show['master_id']] = $show;
}
}
// Start rendering calendar.
$calendar = "\r\n".'<table class="EventCalendar ui-widget-content ui-corner-all"><thead '._HEAD.'><tr><th colspan="7">'.lang('Calendar of events for').' '.date('Y F',strtotime($_SESSION[SYS_USER]['search']['Month'].'-02')).'</th></tr>'
. "\r\n".'<tr><th>'.lang('Sunday').'</th><th>'.lang('Monday').'</th><th>'.lang('Tuesday').'</th><th>'.lang('Wednesday').'</th><th>'.lang('Thursday').'</th><th>'.lang('Friday').'</th><th>'.lang('Saturday').'</th></tr></thead>'
. '<tbody>';
$dayOfWeekPadding = date('w',$firstDayTimestamp);
if( $dayOfWeekPadding == 0 ) {
$dayOfWeekPadding = 6;
} else {
$dayOfWeekPadding -= 0;
}
if( $dayOfWeekPadding > 0 ) {
$calendar .= "\r\n<tr>";
}
for($day=7; $day < $dayOfWeekPadding; $day++) {
$calendar .= "\r\n<td class='EventCalendar_EmptyDate'> </td>";
}
$timeStamp = $firstDayTimestamp;
while( $timeStamp <= $lastDayTimestamp ) {
if( date('w',$timeStamp) == 0 ) {
$calendar .= "\r\n<tr>";
}
$dateKey = date('Y-m-d',$timeStamp);
$calendar .= "\r\n".'<td class="EventCalendar_Date" id="date'.$dateKey.'" onmouseover="tbl_colorchange(\'date'.$dateKey.'\', \'highlight\');" onmouseout="tbl_colorchange(\'date'.$dateKey.'\', \'normal\');"><span>'.date('d',$timeStamp).'</span>';
if( !empty($aryEvents[$dateKey]) ) {
ksort($aryEvents[$dateKey]);
$count = 0;
foreach( $aryEvents[$dateKey] as $key=>$show ) {
$cssClass = $count++ % 2 ? _ODD : _EVEN;
$rttUrl = ( $show['rtt_id'] > 0 ) ? '&rtt='.$show['rtt_id'] : '';
$strPriceList = (empty($show['pricelist']) ? '' : ' title="'.lang('Prices available').': '.$show['pricelist'].'"');
$calendar .= "\r\n<div {$cssClass}{$strPriceList}><a href='event-details.php?e={$show['master_id']}&date={$dateKey}{$rttUrl}'>{$show['']} # {$show['show_time']}</a></div>";
}
}
$calendar .= '</td>';
if( date('w',$timeStamp) == 0 ) {
$calendar .= "\r\n</tr>";
}
$timeStamp += 86400; // Get the next day (in seconds).
}
$dayOfWeekPadding = date('w',$timeStamp);
if( $dayOfWeekPadding == 0 ) {
$dayOfWeekPadding = 6;
} else if( $dayOfWeekPadding == 0 ) {
$dayOfWeekPadding = 0; // do not have an empty week of padding
} else {
$dayOfWeekPadding = 6 - $dayOfWeekPadding;
}
for($day=0; $day < $dayOfWeekPadding; $day++) {
$calendar .= "\r\n<td class='EventCalendar_EmptyDate'> </td>";
}
if( $dayOfWeekPadding > 0 ) {
$calendar .= "\r\n</tr>";
}
$calendar .= "\r\n</tbody></table>";
if( !empty($cstage['cache']) ) {
$cstage['cache']->save($calendar, $keyEventCalendar);
}
}
return $calendar;
}

Optimize perl code

I have to write to check array ref for more than 3 params. If the value is coming from an array then I have written a foreach loop for that, then chop that and assign to a new variable after concatenating pipe.
Code
if ( defined $args->{hotel} ) {
if ( ref( $args->{hotel} ) eq "ARRAY" ) {
foreach my $hotel ( #{ $args->{hotel} } ) {
$hotel .= $hotel . "|";
}
chop($hotel);
$args->{hotel_name} = $hotel;
} else {
$args->{hotel_name} = $args->{hotel};
}
} else {
$args->{hotel_name} = $hotel;
}
if ( defined $args->{country} ) {
if ( ref( $args->{country} ) eq "ARRAY" ) {
foreach my $country_name ( #{ $args->{country} } ) {
$country_name .= $country_name . "|";
}
chop($country_name);
$args->{country_name} = $country_name;
} else {
$args->{country_name} = $args->{country};
}
} else {
$args->{country_name} = $country_name;
}
if ( defined $args->{city} ) {
if ( ref( $args->{city} ) eq "ARRAY" ) {
foreach my $city ( #{ $args->{city} } ) {
$city .= $city . "|";
}
chop($city);
$args->{city_name} = $city;
} else {
$args->{city_name} = $args->{city};
}
} else {
$args->{city_name} = $city;
}
I want to write a function for this kind of work so that there will be no repetition of same code. Please help me; how can we do this in Perl?
You can write:
sub convert_to_name ($$) { # ($value, $fallback_name)
my ($value, $fallback_name) = #_;
if (defined $value) {
if (ref($value) eq 'ARRAY') {
return join '|', #$value;
} else {
return "$value";
}
} else {
return $fallback_name;
}
}
$args->{'hotel_name'} = convert_to_name $args->{'hotel'}, $hotel;
$args->{'country_name'} = convert_to_name $args->{'country'}, $country;
$args->{'city_name'} = convert_to_name $args->{'city'}, $city;
There appear to be some potential bugs in your code, the biggest centering around reusing variable names at lower scopes.
However, of course you can add an iteration loop to your code that would remove the need for 3 nearly identical sections. The following does that by creating a intermediate hash data structure to relate field names to values.
Note: I also simplified the code by inverting the logic if your first if statement so that all ifs could be at the same level. Additionally, it makes sense to use a join instead of rolling your own such functionality.
my %hash = (
hotel => $hotel,
country => $country_name,
city => $city,
);
while ( my ( $field, $value ) = each %hash ) {
if ( !defined $args->{$field} ) {
$args->{"${field}_name"} = $value;
} elsif ( ref( $args->{$field} ) eq "ARRAY" ) {
$args->{"${field}_name"} = join '|', #{ $args->{$field} };
} else {
$args->{"${field}_name"} = $args->{$field};
}
}
Also, if you're comfortable with the Conditional operator, this can be reduced further. However, some would consider this too cluttered:
while ( my ( $field, $value ) = each %hash ) {
$args->{"${field}_name"} = !defined $args->{$field}
? $value
: ref( $args->{$field} ) eq "ARRAY"
? join( '|', #{ $args->{$field} } )
: $args->{$field};
}

Safe way to establish the initial state again.

Is there a possibility of calling the function main without the function _end_win being called?
#!/usr/bin/env perl
use warnings;
use strict;
use 5.10.0;
use Term::ReadKey;
use constant {
NEXT_getch => -1,
CONTROL_C => 0x03,
CONTROL_D => 0x04,
KEY_ENTER => 0x0d,
KEY_RIGHT => 0x1b5b43,
KEY_LEFT => 0x1b5b44,
};
say main();
sub main {
my $arg = {};
$arg->{handle_out} = *STDOUT;
_init_scr( $arg );
while ( 1 ) {
my $c = _getch( $arg );
if ( ! defined $c ) {
_end_win( $arg );
warn "EOT";
return;
}
next if $c == NEXT_getch;
given ( $c ) {
when ( $c >= 97 && $c <= 122 ) {
print chr $c;
$arg->{string} .= chr $c;
}
when ( $c == KEY_RIGHT ) {
print '>';
$arg->{string} .= '>';
}
when ( $c == KEY_LEFT ) {
print '<';
$arg->{string} .= '<';
}
when ( $c == CONTROL_D ) {
_end_win( $arg );
return;
}
when ( $c == CONTROL_C ) {
_end_win( $arg );
print STDERR "^C";
kill( 'INT', $$ );
return;
}
when ( $c == KEY_ENTER ) {
_end_win( $arg );
return $arg->{string};
}
}
}
}
sub _init_scr {
my ( $arg ) = #_;
$arg->{old_handle} = select( $arg->{handle_out} );
$arg->{backup_flush} = $|;
$| = 1;
Term::ReadKey::ReadMode 'ultra-raw';
}
sub _end_win {
my ( $arg ) = #_;
print "\n\r";
Term::ReadKey::ReadMode 'restore';
$| = $arg->{backup_flush};
select( $arg->{old_handle} );
}
sub _getch {
my ( $arg ) = #_;
my $c1 = ReadKey 0;
return if ! defined $c1;
if ( $c1 eq "\e" ) {
my $c2 = ReadKey 0.10;
if ( ! defined $c2 ) { return NEXT_getch; }
elsif ( $c2 eq 'C' ) { return KEY_RIGHT; }
elsif ( $c2 eq 'D' ) { return KEY_LEFT; }
elsif ( $c2 eq '[' ) {
my $c3 = ReadKey 0;
if ( $c3 eq 'C' ) { return KEY_RIGHT; }
elsif ( $c3 eq 'D' ) { return KEY_LEFT; }
else {
return NEXT_getch;
}
}
else {
return NEXT_getch;
}
}
else {
return ord $c1;
}
}
To ensure that the terminal is reset when your program exits, put the reset code into an END block. For example, you could replace your _end_win sub with:
END {
print "\n\r";
Term::ReadKey::ReadMode 'restore';
}
(I removed the lines resetting $| and the selected output filehandle since the process is exiting anyhow, so they're about to become irrelevant.)
An END block will always run when the program terminates in a "normal" way, such as calling exit or die or hitting the end of the executable code. It does not fire when the process terminates due to receiving a signal; it looks like you're handling the ctrl-C character directly, but you may want to consider adding a %SIG{INT} handler as well, in case someone sends you a kill -2.