Skip to content

Instantly share code, notes, and snippets.

@ka215
Created March 23, 2021 08:26
Show Gist options
  • Save ka215/ec56aa06928adadb93c3662806c98e98 to your computer and use it in GitHub Desktop.
Save ka215/ec56aa06928adadb93c3662806c98e98 to your computer and use it in GitHub Desktop.
jQuery.Timeline Demo as early type of scheduler type
<?php
// Const
define( 'USE_LOCAL_TIMEZONE', true );
// Local Valiables
try {
// Get now datetime by using DateTimeImmutable Object
$timezone = USE_LOCAL_TIMEZONE ? date_default_timezone_get() : 'UTC';
// $timezone = 'Asia/Tokyo';
// $timezone = 'Europe/London';
$current_datetime = new \DateTimeImmutable( 'now', new \DateTimeZone( $timezone ) );
} catch ( Exception $e ) {
echo $e->getMessage();
exit( 1 );
}
$auto_datetime = true;
$default_timeline_span = 3;
$_start_datetime = $current_datetime->modify( '-1 day' );
$_end_datetime = intval( $default_timeline_span ) > 0 ? $current_datetime->modify( sprintf( '+%d month', $default_timeline_span ) ) : 'auto';
$date_range = [
'start' => $_start_datetime->format( 'Y-m-d' ),
'end' => $_end_datetime === 'auto' ? $_end_datetime : $_end_datetime->format( 'Y-m-d' )
];
$user_locale = 'en-US';
$is_point_type = false;
$is_ruler_colored = false;
$ruler_top_color = randColorSet( 141 );
$ruler_bottom_color = randColorSet( 141 );
$auto_put_events = true;
$default_timeline_rows = $auto_put_events ? 6 : 'auto';
$sidebar_type = 1;
$scale = '';
// Plugin Default Settings
$defaults = [
'type' => (bool) $is_point_type ? 'point' : 'bar',
'startDatetime' => $date_range['start'],
'endDatetime' => $date_range['end'],
'scale' => ! empty( $scale ) ? $scale : 'day',
'rows' => $default_timeline_rows,
'minGridSize' => 48,
'headline' => [
'display' => true,
'title' => 'Demo of jQuery.Timeline',
'range' => true,
'locale' => $user_locale,
'format' => [
'timeZone' => $timezone,
],
],
'footer' => [
'display' => true,
'content' => '<small>&copy; MAGIC METHODS '. date( 'Y' ) .'</small>',
'range' => true,
'locale' => $user_locale,
'format' => [
'timeZone' => $timezone,
],
],
'sidebar' => [
'sticky' => true,
'list' => [
'<a name="chr-01" href="#chr-01"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-001.png' ) .');"></span> Josiah Hickman</a>',
'<a name="chr-02" href="#chr-02"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-002.png' ) .');"></span> Louis Jarvis</a>',
'<a name="chr-03" href="#chr-03"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-003.png' ) .');"></span> Dale Williams</a>',
'<a name="chr-04" href="#chr-04"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-004.png' ) .');"></span> Eddie Lopez</a>',
'<a name="chr-05" href="#chr-05"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-005.png' ) .');"></span> Cleo Patel</a>',
'<a name="chr-06" href="#chr-06"><span class="thumbnail circled" style="background-image:url('. loadAsset( 'imgs/portrait-thumb-006.png' ) .');"></span> Owen Richardson</a>',
],
],
'ruler' => [
'top' => [
// 'lines' => [ 'millennium', 'century', 'decade', 'lustrum', 'year', 'month', 'week', 'day', 'weekday', 'hour', 'minute', 'second' ],
'lines' => [ 'year', 'month', 'day', 'weekday' ],
'height' => 26,
'fontSize' => 13,
'color' => $is_ruler_colored ? $ruler_top_color['text'] : '#777777',
'background' => $is_ruler_colored ? $ruler_top_color['bg'] : '#FFFFFF',
'locale' => $user_locale,
'format' => [
'timeZone' => $timezone, 'hour12' => false, 'year' => 'numeric', 'month' => 'long', 'day' => 'numeric', 'weekday' => 'short',
],
],
'bottom' => [
'lines' => [ 'week', 'year' ],
'color' => $is_ruler_colored ? $ruler_bottom_color['text'] : '#777777',
'background' => $is_ruler_colored ? $ruler_bottom_color['bg'] : '#FFFFFF',
'locale' => $user_locale,
'format' => [
'timeZone' => $timezone, 'hour12' => false, 'year' => 'numeric', 'week' => 'ordinal',
],
]
],
'rangeAlign' => 'end',
'eventMeta' => [
'display' => false,
'scale' => 'day',
'locale' => $user_locale,
'format' => [
'timeZone' => $timezone,
],
'content' => '',
],
'reloadCacheKeep' => false,
'zoom' => false,
'debug' => false,
];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Demo of jQuery.Timeline</title>
<!-- Bootstrap 4.1.3 -->
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!-- Font Awesome latest 5.3.1 -->
<link rel="stylesheet" href="//use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<!-- jQuery Timeline -->
<link href="//cdn.jsdelivr.net/gh/ka215/jquery.timeline@main/dist/jquery.timeline.min.css" rel="stylesheet">
<link rel="stylesheet" href="./dist/jquery.timeline.demo.css?v=<?= filemtime( './dist/jquery.timeline.demo.css' ); ?>">
<style>
.thumbnail { height: 32px; width: 32px; margin: auto 1em auto 0; background-repeat: no-repeat; background-size: cover; background-position: 50% 50%; }
.thumbnail.circled { border-radius: 50%; }
#myModal .modal-body { opacity: 0; transition: all 0.5s ease; }
#myModal .jqtl-event-view { display: flex; }
#myModal .jqtl-event-view > * { width: 50%; }
#notices p { margin-bottom: 0; }
#notice-php label, #notice-js label { font-weight: 600; margin-right: 1em; }
/* .popover { box-shadow: 0 1px 0 4px rgba(51,51,51,0.05); } */
.popover-body { color: #444; }
.popover-body b { font-weight: 500; color: #212529; }
</style>
</head>
<body>
<div class="container-fluid">
<section class="row">
<div class="col-12">
<div id="my-timeline" class="test-timeline test-1">
<ul class="timeline-events">
<?php
if ( $auto_put_events ) :
$events = generate_random_events( $date_range['start'], 30, 90, $default_timeline_rows, 60 * 24, 60 * 24 * 3 );
if ( $defaults['type'] === 'bar' ) :
foreach ( $events as $_evt ) {
printf( '<li data-timeline-node=\'%s\'></li>', json_encode( $_evt ) );
}
else :
foreach ( $events as $_i => $_evt ) {
$_evt['id'] = $_i + 1;
if ( $_i > 0 && mt_rand( 0, 1 ) == 1 ) {
$_evt['relation'] = [
'before' => $_i > 0 ? $_i : -1,
'curve' => mt_rand( 0, 1 ),
'linesize' => mt_rand( 1, 6 ),
'linecolor' => randColorSet()['bg'],
];
}
printf( '<li data-timeline-node=\'%s\'></li>', json_encode( $_evt ) );
}
endif;
else : ?>
<li>invalid event</li>
<li data-timeline-node="">invalid event</li>
<li data-timeline-node="{}">invalid event</li>
<li data-timeline-node="{start:'2019-3-18 9:50',end:'2019-12-19 19:15',content:'The start point and the end point are out of range (before the time line range)'}">Event outside the range</li>
<li data-timeline-node="{eventId:'7',start:'2019-5-20 5:35',end:'2019-5-23 6:15',bgColor:'#FDE'}"><label class="event-label">Label only</label></li>
<li data-timeline-node="{eventId:0,start:'2019-5-23 9:50',end:'2019-5-26 18:50'}"><div class="event-content">Content only</div></li>
<li data-timeline-node="{eventId:1,start:'2019-5-26 00:00:00',end:'2019-12-31 23:59:59',row:2,label:'Label of attribute',content:'Content of attribute',bgColor:'#CFC',color:'#33E'}">End point out of range</li>
<li data-timeline-node="{eventId:4,start:'2019-5-25 12:00:00',end:'2019-5-26 22:59:59',row:3,bgColor:'#CCF',color:'#E3E'}"><p class="event-label">Label of child element</p><p class="event-content">Content of child element</p></li>
<li data-timeline-node="{eventId:null,start:'2019-3-26 10:03:48',end:'2019-3-27 13:21:16',row:4,bgColor:'#89A',color:'#FFF',label:'Label of attribute',content:'Content of attribute',extend:'{toggle:\'popover\',placement:\'top\',trigger:\'hover\'}'}"><p class="event-label">Example of Bootstrap's Popover</p><span class="event-label">"Duplicate" label of child element</span><p class="event-content">The correspondence to the Bootstrap popover became a little easier</p><div class="event-content">"Duplicate" text of child elements</div></li>
<li data-timeline-node="{start:'2019-1-01 00:00:00',end:'2019-3-24 23:59:59',row:4,bgColor:'#DEF',extend:{toggle:'modal',target:'#myModal'}}"><h4 class="event-label">Example of Bootstrap's Modal</h4><p class="event-content">Also, this event has its starting point outside the range.</p></li>
<li data-timeline-node="{start:'2019-4-01 00:00:00',end:'2019-4-9 23:59:59',row:5,bgColor:'#CC8',callback:'$(\'#myModal\').modal()'}"><h4 class="event-label">Example of Bootstrap's Modal (2)</h4><p class="event-content">Also, this event has its starting point outside the range.</p></li>
<li data-timeline-node="{eventId:1,start:'2019-8-28 5:48',end:'2019-9-28 6:37',row:6,callback:'$(document).localFunction( event )'}"><div class="event-content">Start point out of range (after timeline range)</div></li>
<li data-timeline-node="{start:'2019-3-15 13:05',end:'2019-4-2 16:27',row:7,bgColor:'#9DC',image:'imgs/thumb_014.png',rangeMeta:'Individual meta'}"><div class="event-content">Event with image</div></li>
<li data-timeline-node="{start:'0079-10-18 9:50',end:'0079-12-18 19:15',content:'Sample'}">One year war broke out</li>
<li data-timeline-node="{id:14,start:'2019-3-15 10:50',content:'Size is \"large\"',size:'large',relation:{before:-1}}">Events set only at the start point</li>
<li data-timeline-node="{id:15,start:'2019-3-17 13:45',row:2,content:'Size is \"normal\"',size:'normal',relation:{before:16,curve:'lb'}}">Pointer event (1)</li>
<li data-timeline-node="{id:16,start:'2019-3-21 10:50',row:2,content:'Size is \"small\"',size:'small',relation:{before:15,after:-1,lineSize:8,color:'red'}}">Pointer event (2)</li>
<li data-timeline-node="{id:17,start:'2019-3-23 3:45',row:3,content:'Size is 4 pixel',size:4,bdColor:'blue',relation:{before:16,lineColor:'blue',size:1}}">Pointer event (3)</li>
<li data-timeline-node="{id:18,start:'2019-3-24 0:00',row:4,content:'Size is undefined',relation:{before:17,curve:'lb'}}">Pointer event (4)</li>
<?php
endif; ?>
</ul>
</div><!-- /#my-timeline -->
</div><!-- /.col -->
</section><!-- /.row -->
<div id="copyright" class="text-center m-2">
&copy; <?= date('Y') ?> Monaural Sound ka2.org, Powered by MAGIC METHODS
</div>
</div><!-- /.container-fluid -->
<div class="modal fade" id="myModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="jqtl-event-view"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div><!-- /.modal -->
<div class="custom-loader"></div>
<!-- REQUIRED JS SCRIPTS -->
<!-- jQuery (latest 3.3.1) -->
<script defer src="//code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script defer src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<!-- -->
<!-- Bootstrap 4.1.3 -->
<script defer src="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<!-- jQuery Timeline -->
<script defer src="//cdn.jsdelivr.net/gh/ka215/jquery.timeline@main/dist/jquery.timeline.min.js"></script>
<!-- local scripts -->
<script>
window.addEventListener('load',function(){
const dt = new Date()
let defaults = <?= json_encode( $defaults ) ?>,
overrides = {
//startDatetime: /*'2019-3-1', */'2019-10-01', //'2019-04-01 00:00', //'184-11-30',
//endDatetime: /*'2019-4-1', */'2019-11-01', //'184-12-31',
scale: '<?= ! empty( $scale ) ? $scale : 'day' ?>',
rows: 'auto',
minGridSize: 48,
headline: { title: 'Members Schedule' },
ruler: {
top: {
lines: [ 'year', 'month', 'week', 'day', 'weekday' ],
// locale: 'en-GB',
format: { hour12: false, year: 'numeric', month: 'numeric', week: 'ordinal', day: 'numeric', weekday: 'short', hour: 'numeric' }
},
/*
bottom: {
lines: [ 'day', 'month', 'year' ],
// locale: 'en-GB',
format: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'short' }
}
*/
},
effects: {
hoverEvent: true,
},
reloadCacheKeep: true,
zoom: false,
debug: false
},
mcu_options = Object.assign( defaults, overrides )
$('#my-timeline').Timeline( mcu_options )
.Timeline('initialized', function(e,v){
$('.jqtl-headline-wrapper').append('<div><a href="/" class="btn btn-secondary btn-sm">&laquo; Home</a></div>')
//$('[data-toggle="popover"]').popover()
})
$('#myModal').on('shown.bs.modal', function(){
$(this).find('.modal-title').empty().append( $(this).find('.jqtl-event-title').html() ).end()
.find('.jqtl-event-title').remove().end()
.find('.modal-body').css('opacity','1')
})
},false);
</script>
</body>
</html>
<?php
// Utility
/**
* Generate random events
*
* @param string $start_datetime (reqired)
* @param int $number (optional; defaults to 10 events)
* @param int $timeline_span (optional; defaults to 3 days)
* @param int $timeline_rows (optional; defaults to 5)
* @param int $min_interval_minute (optional; defaults to 15 minutes)
* @param int $max_interval_minute (optional; defaults to 60 * 12 = 720 minutes)
*
* @return array $events
*/
function generate_random_events( $start_datetime, $number = 10, $timeline_span = 3, $timeline_rows = 5, $min_interval_minute = 15, $max_interval_minute = 720 ) {
$events = [];
$number = (int) $number > 0 ? (int) $number : 10;
$_min_date = new \DateTimeImmutable( $start_datetime );
$base_text = <<<EOL
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vel vehicula nibh, a accumsan nisi. Aliquam tempor augue imperdiet, finibus lectus eget, mattis mi. Vestibulum vel ligula lorem. Vestibulum sapien arcu, dictum id pulvinar aliquam, porttitor eu lacus. Maecenas viverra eleifend posuere. Etiam ligula tellus, lobortis id pharetra sed, porttitor vel urna. Cras dictum nunc tempus, placerat dolor lacinia, vestibulum risus. Nulla a tincidunt est, vel feugiat felis. Ut tincidunt placerat tempor. Curabitur leo est, posuere pharetra enim in, tristique dapibus justo. Integer hendrerit est non metus auctor, eu sollicitudin felis facilisis. Maecenas at scelerisque nibh.
Nunc porttitor est urna, vel scelerisque ipsum commodo sed. Morbi eros nulla, hendrerit nec convallis non, lacinia vitae dui. Etiam molestie nisl eget nibh posuere, ac dictum tortor elementum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris dictum dui ut feugiat pharetra. Vivamus consequat massa magna, in ultrices eros bibendum at. Donec cursus et erat a rutrum. Etiam dolor magna, auctor in porta quis, pretium et risus. Ut felis elit, ornare sed mauris ut, interdum imperdiet urna. Morbi commodo neque nec mauris mattis, nec elementum massa pulvinar. Vivamus suscipit, neque ac sagittis maximus, magna turpis pulvinar sem, et elementum urna nisi vitae diam. Vivamus pretium pulvinar laoreet. Proin tortor sapien, viverra vel arcu sed, accumsan congue lectus. Vestibulum sem arcu, fermentum varius laoreet a, elementum nec ipsum. Sed consectetur nibh justo, congue porttitor metus laoreet eget.
Pellentesque porttitor ac libero ac mattis. Aliquam faucibus mattis dolor, tempus fermentum orci fermentum eu. Vestibulum ultrices vulputate aliquet. Donec ligula lacus, dictum vitae euismod ac, vulputate sed nibh. Suspendisse rutrum urna vitae vulputate hendrerit. Maecenas vestibulum erat ipsum, quis accumsan nunc mollis ut. Vestibulum faucibus tellus sed mollis consequat. Maecenas et nulla quam. Morbi quis diam est. Mauris nisl ex, consectetur a molestie sed, ultricies vitae nunc. Cras id arcu sed quam dignissim laoreet. Nam sit amet ex non mauris ornare varius.
Nullam eu diam ligula. Fusce a dolor dui. Vivamus neque metus, convallis at neque a, imperdiet placerat turpis. Proin sed turpis et tellus cursus ultricies. Etiam iaculis facilisis leo eu ullamcorper. Integer sed accumsan quam. Integer eros mi, iaculis vestibulum nisi et, porta consequat odio. Vestibulum molestie, lorem vitae convallis ornare, libero purus ullamcorper nibh, at bibendum ex lectus quis erat.
Aenean dolor dui, vehicula eu porttitor quis, euismod quis turpis. Morbi porta nisi non porttitor efficitur. Cras finibus, mauris nec aliquam hendrerit, massa est posuere dui, ut congue felis dui et mauris. Maecenas convallis dui in purus lacinia condimentum. In non sagittis odio. Duis scelerisque porta feugiat. Proin sit amet odio et sapien volutpat blandit. Suspendisse at nisl non nulla interdum bibendum. Curabitur consequat ut risus nec rhoncus. Nam nec justo dapibus, consectetur eros ut, consequat ex. Nulla vitae eleifend lectus. Phasellus commodo lectus a ipsum posuere, in rutrum leo auctor. Nulla facilisi. Aliquam posuere, massa nec commodo euismod, velit odio ullamcorper mauris, egestas congue diam libero vitae nibh. In odio elit, tincidunt nec eros sit amet, pulvinar hendrerit nibh. Ut porta maximus ex vel cursus.
EOL;
$text_seeds = explode( ' ', str_replace( [ "\r", "\n" ], '', $base_text ) );
for ( $i = 0; $i < $number; $i++ ) {
$event_start_date = $_min_date->modify( sprintf( '+%d minutes', mt_rand( 0, 60 * 24 * $timeline_span ) ) );
$_interval_string = sprintf( 'PT%dM', mt_rand( (int) $min_interval_minute, (int) $max_interval_minute ) );
$elapsed_date = $event_start_date->add( new \DateInterval( $_interval_string ) );
$_sdt = $event_start_date->format( 'Y-m-d H:i' );
$_edt = $elapsed_date->format( 'Y-m-d H:i' );
$_erw = mt_rand( 1, $timeline_rows );
$_label_seeds = array_rand( array_flip( $text_seeds ), 16 );
$_label_hash = hash( 'crc32b', "$_sdt$_edt$_erw" );
$_label_elms = [];
foreach ( str_split( $_label_hash ) as $_str ) {
$_label_elms[] = $_label_seeds[hexdec( strtolower( $_str ) )];
}
$_body_seeds = array_rand( array_flip( $text_seeds ), 16 );
$_body_hash = hash( 'sha1', "$_sdt$_edt$_erw" );
$_body_elms = [];
foreach ( str_split( $_body_hash ) as $_str ) {
$_body_elms[] = $_body_seeds[hexdec( strtolower( $_str ) )];
}
// $_bgcolor = '#' . substr( md5( mt_rand() ), 0, 6 );
$colorset = randColorSet( 141 );
array_push( $events, [
'start' => $_sdt,
'end' => $_edt,
'row' => $_erw,
'bgColor' => $colorset['bg'],
'color' => $colorset['text'],
'label' => ucfirst( str_replace( [ '.', ',' ], '', trim( implode( ' ', $_label_elms ) ) ) ),
'content' => ucfirst( trim( implode( ' ', $_body_elms ), " ." ) ) . '.',
] );
}
return $events;
}
/**
* Retrieve a color set of random background color and text color.
* It is optimized for a text color with high readability according to the luminance of the background color.
*
* @param int $luminance (optional; Luminance as a branch point of light and darkness is in the range of 0 to 255)
*
* @return array $colorset (has keys of "bg" and "text")
*/
function randColorSet( $luminance = 128 ) {
$rgb = [ 'r' => 0, 'g' => 0, 'b' => 0 ];
$colorset = [ 'bg' => '#', 'text' => '#FFFFFF' ];
foreach( $rgb as $_key => $_val ) {
$rgb[$_key] = mt_rand( 0, 255 );
$colorset['bg'] .= str_pad( dechex( $rgb[$_key] ), 2, '0', STR_PAD_LEFT );
}
$yuv = 0.299 * $rgb['r'] + 0.587 * $rgb['g'] + 0.114 * $rgb['b'];
if ( $yuv >= $luminance ) {
$colorset['text'] = '#101010';
}
return $colorset;
}
function loadAsset( $file_relative_path ) {
if ( file_exists( './'. $file_relative_path ) ) {
return $file_relative_path;
} else {
return '';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment