@@ -367,6 +367,160 @@ FlareTail.helpers.kbd.dispatch = function ($target, key) {
367
367
$target . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key } ) ) ;
368
368
} ;
369
369
370
+ /* ------------------------------------------------------------------------------------------------------------------
371
+ * Date & Time
372
+ * ------------------------------------------------------------------------------------------------------------------ */
373
+
374
+ FlareTail . helpers . datetime = { } ;
375
+
376
+ FlareTail . helpers . datetime . options = new Proxy ( {
377
+ relative : false ,
378
+ timezone : 'local' ,
379
+ updater_enabled : false ,
380
+ updater_interval : 60 // seconds
381
+ } , {
382
+ set : ( obj , prop , value ) => {
383
+ let dt = FlareTail . helpers . datetime ;
384
+
385
+ obj [ prop ] = value ;
386
+
387
+ // Update timezone & format on the current view
388
+ dt . update_elements ( ) ;
389
+
390
+ // Start or stop the timer if the relative option is changed
391
+ if ( prop === 'relative' || prop === 'updater_enabled' ) {
392
+ if ( ! document . hidden && dt . options . relative && dt . options . updater_enabled && ! dt . updater ) {
393
+ dt . start_updater ( ) ;
394
+ } else if ( dt . updater ) {
395
+ dt . stop_updater ( ) ;
396
+ }
397
+ }
398
+
399
+ return true ;
400
+ }
401
+ } ) ;
402
+
403
+ FlareTail . helpers . datetime . format = function ( str , options = { } ) {
404
+ options . relative = options . relative !== undefined ? options . relative : this . options . relative ;
405
+ options . simple = options . simple || false ;
406
+ options . timezone = options . timezone || this . options . timezone ;
407
+
408
+ let now = new Date ( ) ,
409
+ date = new Date ( str ) ,
410
+ delta = now - date ,
411
+ shifted_date ;
412
+
413
+ if ( options . relative ) {
414
+ let patterns = [
415
+ [ 1000 * 60 * 60 * 24 * 365 , '%dyr' , 'Last year' , '%d years ago' ] ,
416
+ [ 1000 * 60 * 60 * 24 * 30 , '%dmo' , 'Last month' , '%d months ago' ] ,
417
+ [ 1000 * 60 * 60 * 24 , '%dd' , 'Yesterday' , '%d days ago' ] ,
418
+ [ 1000 * 60 * 60 , '%dh' , '1 hour ago' , '%d hours ago' ] ,
419
+ [ 1000 * 60 , '%dm' , '1 minute ago' , '%d minutes ago' ] ,
420
+ [ 1000 , '%ds' , 'Just now' , '%d seconds ago' ] ,
421
+ [ 0 , 'Now' , 'Just now' , 'Just now' ] // Less than 1 second
422
+ ] ;
423
+
424
+ let format = ( ms , simple , singular , plural ) => {
425
+ let value = Math . floor ( delta / ms ) ;
426
+
427
+ return ( options . simple ? simple : value === 1 ? singular : plural ) . replace ( '%d' , value ) ;
428
+ } ;
429
+
430
+ for ( let pattern of patterns ) if ( delta > pattern [ 0 ] ) {
431
+ return format ( ...pattern ) ;
432
+ }
433
+ }
434
+
435
+ // Timezone conversion
436
+ // TODO: Rewrite this once the timezone support is added to the ECMAScript Intl API (Bug 837961)
437
+ // TODO: Get the timezone of the Bugzilla instance, instead of hardcoding PST
438
+ if ( options . timezone !== 'local' ) {
439
+ shifted_date = this . get_shifted_date ( date , options . timezone === 'PST' ? - 8 : 0 ) ;
440
+ }
441
+
442
+ if ( options . simple && date . getFullYear ( ) === now . getFullYear ( ) && date . getMonth ( ) === now . getMonth ( ) ) {
443
+ let dates = now . getDate ( ) - date . getDate ( ) ;
444
+
445
+ if ( dates === 0 ) {
446
+ return ( shifted_date || date ) . toLocaleFormat ( '%R' ) ;
447
+ }
448
+
449
+ if ( dates === 1 ) {
450
+ return 'Yesterday' ;
451
+ }
452
+ }
453
+
454
+ return ( shifted_date || date ) . toLocaleFormat ( options . simple ? '%b %e' : '%Y-%m-%d %R' ) ;
455
+ } ;
456
+
457
+ FlareTail . helpers . datetime . get_shifted_date = function ( date , offset ) {
458
+ let dst = Math . max ( ( new Date ( date . getFullYear ( ) , 0 , 1 ) ) . getTimezoneOffset ( ) ,
459
+ ( new Date ( date . getFullYear ( ) , 6 , 1 ) ) . getTimezoneOffset ( ) )
460
+ > date . getTimezoneOffset ( ) ,
461
+ utc = date . getTime ( ) + ( date . getTimezoneOffset ( ) + ( dst ? 60 : 0 ) ) * 60000 ;
462
+
463
+ return new Date ( utc + offset * 3600000 ) ;
464
+ } ;
465
+
466
+ FlareTail . helpers . datetime . fill_element = function ( $time , value , options = null ) {
467
+ if ( ! options ) {
468
+ options = {
469
+ relative : $time . dataset . relative ? JSON . parse ( $time . dataset . relative ) : undefined ,
470
+ simple : $time . dataset . simple ? JSON . parse ( $time . dataset . simple ) : undefined
471
+ } ;
472
+ }
473
+
474
+ $time . dateTime = value ;
475
+ $time . textContent = this . format ( value , FlareTail . helpers . object . clone ( options ) ) ;
476
+ $time . title = ( new Date ( value ) ) . toString ( ) ;
477
+
478
+ if ( options . relative !== undefined ) {
479
+ $time . dataset . relative = options . relative ;
480
+ }
481
+
482
+ if ( options . simple !== undefined ) {
483
+ $time . dataset . simple = options . simple ;
484
+ }
485
+
486
+ return $time ;
487
+ } ;
488
+
489
+ FlareTail . helpers . datetime . update_elements = function ( ) {
490
+ for ( let $time of document . querySelectorAll ( 'time' ) ) {
491
+ let data = $time . dataset ,
492
+ time = this . format ( $time . dateTime , {
493
+ relative : data . relative !== undefined ? data . relative === 'true' : this . options . relative ,
494
+ simple : data . simple !== undefined ? data . simple === 'true' : this . options . simple
495
+ } ) ;
496
+
497
+ if ( $time . textContent !== time ) {
498
+ $time . textContent = time ;
499
+ }
500
+ }
501
+ } ;
502
+
503
+ FlareTail . helpers . datetime . start_updater = function ( ) {
504
+ this . updater = window . setInterval ( ( ) => this . update_elements ( ) , this . options . updater_interval * 1000 ) ;
505
+ } ;
506
+
507
+ FlareTail . helpers . datetime . stop_updater = function ( ) {
508
+ window . clearInterval ( this . updater ) ;
509
+
510
+ delete this . updater ;
511
+ } ;
512
+
513
+ document . addEventListener ( 'visibilitychange' , event => {
514
+ let dt = FlareTail . helpers . datetime ;
515
+
516
+ if ( ! document . hidden && dt . options . relative && dt . options . updater_enabled && ! dt . updater ) {
517
+ dt . update_elements ( ) ;
518
+ dt . start_updater ( ) ;
519
+ } else if ( dt . updater ) {
520
+ dt . stop_updater ( ) ;
521
+ }
522
+ } ) ;
523
+
370
524
/* ------------------------------------------------------------------------------------------------------------------
371
525
* User Agent
372
526
*
0 commit comments